1. Introduction and Aims

Two datasets were generated using the 10X Genomics Chromium 3’ scRNA-Seq platform:

species experiment_name run_number lane_number sequencer approximate_number_of_cells
pb straight bleed experiment 22252 5 Hiseq 4000 30,000
pb 1:1 mix experiment 24284 1 & 2 Hiseq 2500 5,000

This script details the quality control of the 1:1 mix experiment

This data has been processed using CellRanger into counts tables. This initial analysis gave the following metrics:

Pb 1:1 mix experiment (run #: 24284 lanes 1 and 2 (Hiseq 2500)):

We will load this data in and for each run:

A. Define ‘cells’

B. Filter poor quality cells out

C. Dimensionality Reduction and Clustering

D. Remove Doublets

E. Predict life Cycle Stage (Using Bulk RNA-Seq Correlation)

2. Read in the data

Load the required packages

[1] "Seurat is loaded correctly"
[1] "cowplot is loaded correctly"
[1] "gridExtra is loaded correctly"
[1] "grid is loaded correctly"
[1] "Hmisc is loaded correctly"
[1] "dplyr is loaded correctly"
[1] "scales is loaded correctly"
[1] "ggpubr is loaded correctly"
[1] "patchwork is loaded correctly"

Import GTF file

This will be helpful later on. This contains annotations for each gene:

##Import gtf file:
gtf <- read.table("/Users/Andy/GCSKO/GCSKO_analysis_git/data/Reference/Pberghei.gtf", sep="\t", header = FALSE)
head(gtf)

Read in the Data

## read in 10x output 
tenx5k_raw_data <- Read10X("/Users/Andy/GCSKO/GCSKO_analysis_git/data/10X/tenx_24284")

## Create Seurat object
tenx5k <- CreateSeuratObject(counts = tenx5k_raw_data, min.cells = 0, min.features = 0, project = "GCSKO")
Feature names cannot have underscores ('_'), replacing with dashes ('-')
## add experiment to meta data for merging later
tenx5k@meta.data$experiment <- "tenx5k"

## inspect
tenx5k
An object of class Seurat 
5098 features across 737280 samples within 1 assay 
Active assay: RNA (5098 features, 0 variable features)

3. Defining Cells vs. Background

Plot a knee plot and then use a mixture model to define where the cells vs. background lie

## interesting reference material for this section can be found here: https://hemberg-lab.github.io/scRNA.seq.course/processing-raw-scrna-seq-data.html 

## get the nUMIs
umi_per_barcode <- as.data.frame(tenx5k@meta.data$nCount_RNA)

## remove zeros as these have issues when you log them and make the model later:
umi_per_barcode <- as.data.frame(umi_per_barcode[!(umi_per_barcode$`tenx5k@meta.data$nCount_RNA`==0), ])

## get a rank for each barcode
barcode_rank <- rank(-umi_per_barcode[,1])

## then make into a list
lib_size <- (umi_per_barcode[,1])

## then log this
log_lib_size <- log10(umi_per_barcode[,1])

##plot
#plot(barcode_rank, log_lib_size, xlim=c(1,100000))

## order the barcode ranks
o <- order(barcode_rank)

## reorder the library size, barcode rank by their rank
log_lib_size <- log_lib_size[o]
barcode_rank <- barcode_rank[o]
lib_size <- lib_size[o]

make a mixture model to determine the knee of the plot

## set a seed for the mixture model
set.seed(-92497)

## mixture model calculation 
require("mixtools")
mix <- normalmixEM(log_lib_size)
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
number of iterations= 17 
## plot result
plot(mix, which=2, xlab2="log(mol per cell)")

Find where the distributions intersect (i.e. where cells vs. background is)

## identify where the split between the distributions is
p1 <- dnorm(log_lib_size, mean=mix$mu[1], sd=mix$sigma[1])
p2 <- dnorm(log_lib_size, mean=mix$mu[2], sd=mix$sigma[2])
if (mix$mu[1] < mix$mu[2]) {
    split <- min(log_lib_size[p2 > p1])
} else {
    split <- min(log_lib_size[p1 > p2])
}

## print split
split
[1] 1.724276

View the initial result

## log the barcode rank
log_barcode_rank <- log10(barcode_rank)

## plot
plot(log_barcode_rank, log_lib_size, xlim=c(1,6))
## add the split as a line on the plot
abline(h=split, col="red")

Final Figures:

## make the results of the above functions into a dataframe
df_barcodes <- as.data.frame(cbind(barcode_rank, log_lib_size, lib_size), row.names = NULL)

## add a column for if it is a cell or not
df_barcodes$cell = rownames(df_barcodes) %in% which(df_barcodes$log_lib_size > split)

## change value to a numeric
df_barcodes$cell <- as.numeric(df_barcodes$cell)

## change the 0 to a 2 for ease of handling
df_barcodes$cell[df_barcodes$cell<1] <- 2

## rename the numerics into cells or background
df_barcodes$cell[df_barcodes$cell == 1] <- "Cells"
df_barcodes$cell[df_barcodes$cell == 2] <- "Background"

## extract the cutoff for cells do you can plot the lines
boundary <- as.numeric(sum(df_barcodes$cell == "Cells"))
split <- 10^split

## make the plot
barcode_plot <- ggplot(df_barcodes, aes(x=barcode_rank, y=lib_size, colour = cell, theme_size = 40)) +
  ## make into a dot plot
  geom_point(size = 1, shape = 16) +
  ## make the axis into log and specify breaks
  scale_x_log10(breaks = trans_breaks("log10", function(x) 10^x), labels = trans_format("log10", math_format(10^.x))) +
  ## make the axis into log and specify breaks
  scale_y_log10(breaks = trans_breaks("log10", function(x) 10^x), labels = trans_format("log10", math_format(10^.x))) +
  annotation_logticks() +
  ## change colours of plot
  scale_color_manual(values=c("#bdbdbd", "#5ba43a"), labels = c("Background", "Cells")) +
  ## change aes of legend
  theme(legend.position="bottom", text = element_text(size=25), legend.text=element_text(size=25), axis.text=element_text(size=25)) +
  ## add the lines on the plot
  geom_segment(aes(x = 0, y = split, xend = boundary, yend = split), colour = "black", alpha = 0.01) +
  geom_segment(aes(x = boundary, y = 0, xend = boundary, yend = split), colour = "black", alpha = 0.01) +
  ## change the axis labels
  labs(x = "Barcodes", y = "UMI Counts", colour="Cell Designation") +
  ## change the size of the legend
  guides(colour = guide_legend(override.aes = list(size=10))) +
  ## fix axis
  coord_fixed() +
  ## make it look pretty
  theme_light()

## print the plot
barcode_plot

## save the plot
ggsave("QC_10X_barcode_plot_5k.png", plot = barcode_plot, device = "png", height = 15, width = 15, units = "cm", path = "../images_to_export/")

so the number of cells that is retained is:

Filter the object

## extract the nCount and row names from the Seurat object
upb <- data.frame(nCount_RNA = tenx5k@meta.data$nCount_RNA, row.names = rownames(tenx5k@meta.data))

## make blank column for rank
upb$rank <- NA

## order by nCounts
order.scores <- order(upb$nCount_RNA, decreasing = TRUE)

## add a rank to this column based on the ordered nCount
upb$rank[order.scores] <- 1:nrow(upb)

## inspect
#head(upb)

## make a list of cells to retain in the Seurat object
keep_cells <- rownames(upb[which(upb$rank < 7763),])

## subset Seurat object to include cells and discard background
pb_sex <- subset(tenx5k, cells = keep_cells)

4. Filter Out Poor-Quality Cells

Filter Mitochondrial %

Mitochondrial cell counts

## extract mitochondrial genes 
#mito_genes <- gtf[which(gtf$V3 == "rRNA"),]$V9
#mito_genes <- gsub(";.*","", gsub("gene_id ", "", mito_genes))
#paste("These are the mitochondrial genes")
#head(mito_genes)

## extract mito genes
mito_genes <- pb_sex@assays$RNA@counts@Dimnames[[1]][grep("^PBANKA-MIT",pb_sex@assays$RNA@counts@Dimnames[[1]])]

## plot the genes individually
VlnPlot(object = pb_sex, features = mito_genes, pt.size = 0.01)
All cells have the same value of PBANKA-MIT0010.All cells have the same value of PBANKA-MIT0020.All cells have the same value of PBANKA-MIT0030.All cells have the same value of PBANKA-MIT0040.All cells have the same value of PBANKA-MIT0050.All cells have the same value of PBANKA-MIT0090.All cells have the same value of PBANKA-MIT0130.All cells have the same value of PBANKA-MIT0140.All cells have the same value of PBANKA-MIT0150.All cells have the same value of PBANKA-MIT0160.All cells have the same value of PBANKA-MIT0200.All cells have the same value of PBANKA-MIT0210.All cells have the same value of PBANKA-MIT0230.All cells have the same value of PBANKA-MIT0240.All cells have the same value of PBANKA-MIT0250.All cells have the same value of PBANKA-MIT0260.All cells have the same value of PBANKA-MIT0290.All cells have the same value of PBANKA-MIT0300.All cells have the same value of PBANKA-MIT0320.All cells have the same value of PBANKA-MIT0330.All cells have the same value of PBANKA-MIT0340.All cells have the same value of PBANKA-MIT0370.

## make a percentage mitocondrial for each cell (this will work as long as you filter cells out with zero counts)
pb_sex <- PercentageFeatureSet(pb_sex, pattern = "^PBANKA-MIT", col.name = "percent.mt")

plot percentage mitochondrial

## plot for percentage of mitochondrial reads
v1 <- VlnPlot(object = pb_sex, features = "percent.mt", pt.size = 0.01) +
  ## add a line where we will filter
  geom_abline(intercept = 20, col="blue") +
  ## change labels
  labs(x = "",y = "% Mitochondrial Reads", title = "Mitochondrial per cell") +
  ## remove legend
  theme(legend.position = "none") +
  ## change appearance
  theme_classic() +
  scale_fill_manual(values="grey") +
  #scale_y_continuous(limits = c(0, 100)) +
  theme(legend.position="none", axis.text.x = element_blank(), axis.ticks.x=element_blank(), text = element_text(size=20), legend.text=element_text(size=20), axis.text=element_text(size=20), axis.text.y=element_text(colour="black"))

## print
v1


## save
#ggsave("~/images_to_export/QC_10X_mito_violin.png", plot = QC_mito_violin, device = "png", path = NULL, scale = 1, width = 15, height = 10, units = "cm", dpi = 300, limitsize = TRUE)

In the Smart-seq2 data, we use a threshold of 20%. No cell in our 10X data is higher than 13% and only

cells have a % mitochondrial reads above 5%.

nGenes filter

plot individual violin plots

## nGenes plot
gene_plot_5k <- VlnPlot(object = pb_sex, features = "nFeature_RNA", pt.size = 0.01)

## improve the aesthetics
gene_plot_5k <- gene_plot_5k + 
  geom_abline(intercept = 200, col="blue") +
  labs(x = "",y = "nGene", title = "Genes per cell") +
  theme_classic() +
  scale_fill_manual(values="grey") +
  scale_y_continuous(limits = c(0, 3000)) +
  theme(legend.position="none", axis.text.x = element_blank(), axis.ticks.x=element_blank(), text = element_text(size=20), legend.text=element_text(size=20), axis.text=element_text(size=20), axis.text.y=element_text(colour="black"))
Scale for 'y' is already present. Adding another scale for 'y', which will
replace the existing scale.
## nUMI plot
numi_plot_5k <- VlnPlot(object = pb_sex, features = "nCount_RNA", pt.size = 0.01)

## improve aesthetics
numi_plot_5k <- numi_plot_5k +
  labs(x = "",y = "nUMI", title = "UMIs per cell") +
  theme_classic() +
  scale_fill_manual(values="grey") +
  scale_y_continuous(limits = c(0, 3000)) +
  theme(legend.position="none", axis.text.x = element_blank(), axis.ticks.x=element_blank(), text = element_text(size=20), legend.text=element_text(size=20), axis.text=element_text(size=20), axis.text.y=element_text(colour="black"))
Scale for 'y' is already present. Adding another scale for 'y', which will
replace the existing scale.
## plot together
grid.arrange(gene_plot_5k, numi_plot_5k, ncol = 2, top=textGrob("5K cells 10X", gp=gpar(fontsize=15,font=8)))


## save nGene plot on its own
#ggsave("QC_ngene_plot.pdf", plot = gene1, device = "pdf", height = 5, width = 5, units = "in", path = "/Users/ar19/Desktop/PhD/GCSKO_Analysis")

plot two metrics together

## make a dataframe for important filtering metrics
df <- data.frame(nCount = log10(pb_sex@meta.data$nCount_RNA), nGenes = pb_sex@meta.data$nFeature_RNA, percent_mt = pb_sex@meta.data$percent.mt, experiment = pb_sex@meta.data$experiment)

## plot main dotplot
plot1 <- ggplot(df, aes(x = nCount, y = nGenes)) +
  #geom_point(aes(), size = 0.1) +
  geom_hex(bins = 200) +
  #geom_rug() + 
  scale_y_continuous(name = "Number of Detected Genes") + 
  scale_x_continuous(name = "log10(Number of Total UMI)") + 
  theme_pubr() +
  theme(legend.position = "bottom") +
  geom_hline(yintercept=200)

## plot density plot x
dens <- density(df$nCount)
dt <- data.frame(x=dens$x, y=dens$y)
probs <- c(0, 0.25, 0.5, 0.75, 1)
quantiles <- quantile(df$nCount, prob=probs)
dt$quant <- factor(findInterval(dt$x,quantiles))
dens1 <- ggplot(dt, aes(x,y)) + geom_line() + geom_ribbon(aes(ymin=0, ymax=y, fill=quant)) + scale_x_continuous(breaks=quantiles) + scale_fill_brewer(guide="none") + theme_void() + theme(legend.position = "none")
## plot density plot y
## old method
# dens2 <- ggplot(df, aes(x = nGenes, y = experiment)) +
#   geom_density(alpha = 0.2) +
#   theme_void() +
#   theme(legend.position = "none") +
#   coord_flip()
## new method
dens <- density(df$nGenes)
dt <- data.frame(x=dens$x, y=dens$y)
probs <- c(0, 0.25, 0.5, 0.75, 1)
quantiles <- quantile(df$nGenes, prob=probs)
dt$quant <- factor(findInterval(dt$x,quantiles))
dens2 <- ggplot(dt, aes(x,y)) + geom_line() + geom_ribbon(aes(ymin=0, ymax=y, fill=quant)) + scale_x_continuous(breaks=quantiles) + scale_fill_brewer(guide="none") + theme_void() + theme(legend.position = "none") +
  coord_flip()

## plot together
QC_composite_plot <- dens1 + plot_spacer() + plot1 + dens2 + plot_layout(ncol = 2, nrow = 2, widths = c(4, 1), heights = c(1, 4))

## print
QC_composite_plot


## save
ggsave("/Users/Andy/GCSKO/GCSKO_analysis_git/images_to_export/QC_10X_composite_plot.png", plot = QC_composite_plot, device = "png", path = NULL, scale = 1, width = 15, height = 15, units = "cm", dpi = 300, limitsize = TRUE)

Filtering

The threshold used in the malaria cell atlas was 230 for Pb but this is dependent on sequencing depth etc. We can plot the number of cells recovered for a range of thresholds:

paste("original number of cells =", nrow(pb_sex@meta.data))
paste("with >150 filter =", nrow(pb_sex@meta.data[pb_sex@meta.data$nFeature_RNA > 150, ]))
paste("with >200 filter =", nrow(pb_sex@meta.data[pb_sex@meta.data$nFeature_RNA > 200, ]))
paste("with >230 filter =", nrow(pb_sex@meta.data[pb_sex@meta.data$nFeature_RNA > 230, ]))

Since we have already filtered on nUMI, we will filter with 200.

## number of cells before filtering
pb_sex_pre_filter_nCells <- nrow(pb_sex@meta.data)
## filter object
pb_sex <- subset(pb_sex, subset = nFeature_RNA > 200)
## number of cells after filtering
pb_sex_post_filter_nCells <- nrow(pb_sex@meta.data)
## print results of filtering
paste("number of cells pre-filter", pb_sex_pre_filter_nCells)
[1] "number of cells pre-filter 7762"
paste("number of cells post-filter", pb_sex_post_filter_nCells)
[1] "number of cells post-filter 6631"
paste((pb_sex_pre_filter_nCells - pb_sex_post_filter_nCells), "cells were removed by filtering on number of genes.")
[1] "1131 cells were removed by filtering on number of genes."

5. Dimensionality Reduction and Clustering

Prepare data

## normalise object
pb_sex <- NormalizeData(pb_sex, normalization.method = "LogNormalize", scale.factor = 10000)
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
## find variable genes
pb_sex <- FindVariableFeatures(pb_sex, selection.method = "vst", nfeatures = 2000)
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
## scale the data
all.genes <- rownames(pb_sex)
pb_sex <- ScaleData(pb_sex, features = all.genes)
Centering and scaling data matrix

  |                                                                              
  |                                                                        |   0%
  |                                                                              
  |============                                                            |  17%
  |                                                                              
  |========================                                                |  33%
  |                                                                              
  |====================================                                    |  50%
  |                                                                              
  |================================================                        |  67%
  |                                                                              
  |============================================================            |  83%
  |                                                                              
  |========================================================================| 100%

PCA

## run PCA
pb_sex <- RunPCA(pb_sex, features = VariableFeatures(object = pb_sex))
PC_ 1 
Positive:  PBANKA-1235600, PBANKA-0931300, PBANKA-1214300, PBANKA-1400400, PBANKA-1101100, PBANKA-0205000, PBANKA-0311800, PBANKA-0823400, PBANKA-1242300, PBANKA-1246600 
       PBANKA-0722600, PBANKA-1008500, PBANKA-1308600, PBANKA-0932400, PBANKA-1314800, PBANKA-1300096, PBANKA-1309900, PBANKA-1200096, PBANKA-1145400, PBANKA-0722801 
       PBANKA-1100441, PBANKA-1040521, PBANKA-1326400, PBANKA-1101300, PBANKA-0941300, PBANKA-1210200, PBANKA-1303800, PBANKA-0216061, PBANKA-0406500, PBANKA-1127000 
Negative:  PBANKA-1312700, PBANKA-1224900, PBANKA-0824400, PBANKA-1453000, PBANKA-1115200, PBANKA-1432200, PBANKA-1204200, PBANKA-0703500, PBANKA-0702000, PBANKA-0812600 
       PBANKA-0312700, PBANKA-1128800, PBANKA-1218300, PBANKA-1106500, PBANKA-1430600, PBANKA-1449300, PBANKA-0209300, PBANKA-1232600, PBANKA-1432400, PBANKA-1421500 
       PBANKA-1320800, PBANKA-0620400, PBANKA-1038800, PBANKA-1110700, PBANKA-1336200, PBANKA-0417600, PBANKA-1451100, PBANKA-1143800, PBANKA-1414500, PBANKA-0402700 
PC_ 2 
Positive:  PBANKA-1431500, PBANKA-1431400, PBANKA-0942400, PBANKA-1340400, PBANKA-0934700, PBANKA-1038700, PBANKA-0942300, PBANKA-1449000, PBANKA-1208800, PBANKA-0828900 
       PBANKA-0911000, PBANKA-0306100, PBANKA-0927700, PBANKA-0806800, PBANKA-0416100, PBANKA-0507800, PBANKA-1409600, PBANKA-0411300, PBANKA-0406200, PBANKA-0415800 
       PBANKA-1033700, PBANKA-1361500, PBANKA-0925400, PBANKA-0615200, PBANKA-1108800, PBANKA-0507300, PBANKA-0720100, PBANKA-0920700, PBANKA-1030400, PBANKA-0510600 
Negative:  PBANKA-1317200, PBANKA-1352500, PBANKA-0810700, PBANKA-1225500, PBANKA-1319500, PBANKA-0402500, PBANKA-0827400, PBANKA-1419300, PBANKA-0714000, PBANKA-1315300 
       PBANKA-0105100, PBANKA-1436300, PBANKA-1311400, PBANKA-0417600, PBANKA-0821400, PBANKA-0417200, PBANKA-0204500, PBANKA-1342300, PBANKA-0704900, PBANKA-1415200 
       PBANKA-0605800, PBANKA-1115000, PBANKA-0517600, PBANKA-1329900, PBANKA-1134900, PBANKA-1231300, PBANKA-1233600, PBANKA-1035200, PBANKA-0912500, PBANKA-1363600 
PC_ 3 
Positive:  PBANKA-1400600, PBANKA-1459300, PBANKA-0416000, PBANKA-0304800, PBANKA-0305100, PBANKA-1365200, PBANKA-1443300, PBANKA-0830200, PBANKA-0938400, PBANKA-0827200 
       PBANKA-1137800, PBANKA-1437300, PBANKA-0932200, PBANKA-0506100, PBANKA-1349000, PBANKA-0313800, PBANKA-1113300, PBANKA-0409800, PBANKA-1017100, PBANKA-1117000 
       PBANKA-0408500, PBANKA-1450300, PBANKA-0922500, PBANKA-0934300, PBANKA-0505200, PBANKA-0941800, PBANKA-0915200, PBANKA-0722921, PBANKA-0316600, PBANKA-0519400 
Negative:  PBANKA-1431500, PBANKA-1431400, PBANKA-1449000, PBANKA-0806800, PBANKA-0927700, PBANKA-0911000, PBANKA-1108000, PBANKA-0828900, PBANKA-0601200, PBANKA-1208800 
       PBANKA-1409600, PBANKA-0830900, PBANKA-0942400, PBANKA-1029400, PBANKA-1038700, PBANKA-0102700, PBANKA-1316500, PBANKA-1304500, PBANKA-0521200, PBANKA-1333100 
       PBANKA-0902400, PBANKA-1129600, PBANKA-1119800, PBANKA-0822900, PBANKA-1109600, PBANKA-0301800, PBANKA-1429100, PBANKA-0622000, PBANKA-1038200, PBANKA-0942300 
PC_ 4 
Positive:  PBANKA-0107300, PBANKA-1214300, PBANKA-0814200, PBANKA-0713300, PBANKA-0604300, PBANKA-0405200, PBANKA-0109100, PBANKA-0932200, PBANKA-0823400, PBANKA-1460400 
       PBANKA-1448000, PBANKA-0416500, PBANKA-0938400, PBANKA-0406500, PBANKA-1302800, PBANKA-1130500, PBANKA-1437300, PBANKA-1444100, PBANKA-0211500, PBANKA-1419100 
       PBANKA-0710100, PBANKA-1223100, PBANKA-1235600, PBANKA-1450300, PBANKA-0919600, PBANKA-0916200, PBANKA-1242300, PBANKA-1326400, PBANKA-0313800, PBANKA-0818900 
Negative:  PBANKA-0519000, PBANKA-0519100, PBANKA-0519300, PBANKA-0831000, PBANKA-1349100, PBANKA-1344400, PBANKA-1002400, PBANKA-0713100, PBANKA-0523700, PBANKA-0111000 
       PBANKA-0519400, PBANKA-1349000, PBANKA-1032100, PBANKA-1014500, PBANKA-0932000, PBANKA-0804500, PBANKA-1315700, PBANKA-0501400, PBANKA-1425900, PBANKA-0519200 
       PBANKA-1101400, PBANKA-0509600, PBANKA-0619700, PBANKA-1035400, PBANKA-0417800, PBANKA-1210600, PBANKA-1327100, PBANKA-0523800, PBANKA-0307500, PBANKA-1117200 
PC_ 5 
Positive:  PBANKA-0501600, PBANKA-1240600, PBANKA-1002600, PBANKA-0707700, PBANKA-0112200, PBANKA-1002400, PBANKA-0208900, PBANKA-0307500, PBANKA-0812200, PBANKA-1423000 
       PBANKA-0832800, PBANKA-1119600, PBANKA-1409200, PBANKA-0716900, PBANKA-0100400, PBANKA-1364400, PBANKA-1347000, PBANKA-0915000, PBANKA-0819600, PBANKA-1319000 
       PBANKA-1212500, PBANKA-1400091, PBANKA-0900900, PBANKA-1202000, PBANKA-1345900, PBANKA-0311500, PBANKA-1008600, PBANKA-0911700, PBANKA-1349100, PBANKA-0519200 
Negative:  PBANKA-1443300, PBANKA-0925600, PBANKA-0306700, PBANKA-1106500, PBANKA-0907200, PBANKA-0304800, PBANKA-1217400, PBANKA-1107600, PBANKA-1349000, PBANKA-1319300 
       PBANKA-1313100, PBANKA-0312500, PBANKA-0611700, PBANKA-1113300, PBANKA-0208000, PBANKA-1241800, PBANKA-0305100, PBANKA-1438000, PBANKA-1339300, PBANKA-0304400 
       PBANKA-0833300, PBANKA-0206300, PBANKA-0207500, PBANKA-0519400, PBANKA-0715300, PBANKA-1360000, PBANKA-0112100, PBANKA-1340500, PBANKA-0915200, PBANKA-1416800 
## plot 
DimPlot(pb_sex, reduction = "pca")


## elbow plot
ElbowPlot(pb_sex, ndims = 30, reduction = "pca")

UMAP

## run UMAP
pb_sex <- RunUMAP(pb_sex, dims = 1:10, seed.use = 1234, n.neighbors = 150)
20:34:58 UMAP embedding parameters a = 0.9922 b = 1.112
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by ‘BiocGenerics’
20:34:58 Read 6631 rows and found 10 numeric columns
20:34:58 Using Annoy for neighbor search, n_neighbors = 150
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by ‘BiocGenerics’
20:34:58 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
20:35:00 Writing NN index file to temp file /var/folders/wj/rztzclxn1t10cl2sk0plbf3r0000gn/T//RtmpXGhNV1/file433740c39e54
20:35:00 Searching Annoy index using 1 thread, search_k = 15000
20:35:10 Annoy recall = 100%
20:35:11 Commencing smooth kNN distance calibration using 1 thread
20:35:15 Initializing from normalized Laplacian + noise
20:35:18 Commencing optimization for 500 epochs, with 1163176 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
20:35:34 Optimization finished
## plot
DimPlot(pb_sex, reduction = "umap", group.by = "ident", label = TRUE)


## These are the parameters used in the merge UMAP
#pb_sex <- RunUMAP(pb_sex, reduction = "pca", dims = 1:10, n.neighbors = 150, seed.use = 1234, min.dist = 0.4, repulsion.strength = 0.03, local.connectivity = 150)
#DimPlot(pb_sex, reduction = "umap", group.by = "ident", label = TRUE)

colour with a few marker genes:

# PBANKA-0515000 - p25 - female
# PBANKA-1212600 - HAP2 - male
# PBANKA-0600600 - NEK3 - male
# PBANKA-0831000 - MSP1 - late asexual
# PBANKA-1315700 - RON2 - (asexuals and some male?)
# PBANKA-0416100 - dynenin heavy chain - male - used in 820 line
# PBANKA-1319500 - CCP2 - female - used in 820 line 
# PBANKA-1437500 - AP2-G - seuxal commitment gene
# PBANKA-1102200 - MSP8 - early asexual (from Bozdech paper)

FeaturePlot(pb_sex, features = c("PBANKA-0515000", "PBANKA-1212600","PBANKA-0600600", "PBANKA-0831000", "PBANKA-1315700", "PBANKA-0416100", "PBANKA-1319500", "PBANKA-1437500", "PBANKA-1102200"))

Clustering

pb_sex <- FindNeighbors(pb_sex, dims = 1:21)
pb_sex <- FindClusters(pb_sex, resolution = 1)

6. Remove Doublets

DoubletFinder

DoubletFinder function

## DoubletFinder should be able to be installed and run as so:
#devtools::install_github('chris-mcginnis-ucsf/DoubletFinder')
#library(doubletFinder) #allows removal of doublets
## but there seems to be some problems with this so we will run it from the github code

## the doublet finder function
doubletFinder_v3 <- function(seu, PCs, pN = 0.25, pK, nExp, reuse.pANN = FALSE, sct = FALSE) {
  require(Seurat); require(fields); require(KernSmooth)

  ## Generate new list of doublet classificatons from existing pANN vector to save time
  if (reuse.pANN != FALSE ) {
    pANN.old <- seu@meta.data[ , reuse.pANN]
    classifications <- rep("Singlet", length(pANN.old))
    classifications[order(pANN.old, decreasing=TRUE)[1:nExp]] <- "Doublet"
    seu@meta.data[, paste("DF.classifications",pN,pK,nExp,sep="_")] <- classifications
    return(seu)
  }

  if (reuse.pANN == FALSE) {
    ## Make merged real-artifical data
    real.cells <- rownames(seu@meta.data)
    data <- seu@assays$RNA@counts[, real.cells]
    n_real.cells <- length(real.cells)
    n_doublets <- round(n_real.cells/(1 - pN) - n_real.cells)
    print(paste("Creating",n_doublets,"artificial doublets...",sep=" "))
    real.cells1 <- sample(real.cells, n_doublets, replace = TRUE)
    real.cells2 <- sample(real.cells, n_doublets, replace = TRUE)
    doublets <- (data[, real.cells1] + data[, real.cells2])/2
    colnames(doublets) <- paste("X", 1:n_doublets, sep = "")
    data_wdoublets <- cbind(data, doublets)

    ## Store important pre-processing information
    orig.commands <- seu@commands

    ## Pre-process Seurat object
    if (sct == FALSE) {
      print("Creating Seurat object...")
      seu_wdoublets <- CreateSeuratObject(counts = data_wdoublets)

      print("Normalizing Seurat object...")
      seu_wdoublets <- NormalizeData(seu_wdoublets,
                                     normalization.method = orig.commands$NormalizeData.RNA@params$normalization.method,
                                     scale.factor = orig.commands$NormalizeData.RNA@params$scale.factor,
                                     margin = orig.commands$NormalizeData.RNA@params$margin)

      print("Finding variable genes...")
      seu_wdoublets <- FindVariableFeatures(seu_wdoublets,
                                            selection.method = orig.commands$FindVariableFeatures.RNA$selection.method,
                                            loess.span = orig.commands$FindVariableFeatures.RNA$loess.span,
                                            clip.max = orig.commands$FindVariableFeatures.RNA$clip.max,
                                            mean.function = orig.commands$FindVariableFeatures.RNA$mean.function,
                                            dispersion.function = orig.commands$FindVariableFeatures.RNA$dispersion.function,
                                            num.bin = orig.commands$FindVariableFeatures.RNA$num.bin,
                                            binning.method = orig.commands$FindVariableFeatures.RNA$binning.method,
                                            nfeatures = orig.commands$FindVariableFeatures.RNA$nfeatures,
                                            mean.cutoff = orig.commands$FindVariableFeatures.RNA$mean.cutoff,
                                            dispersion.cutoff = orig.commands$FindVariableFeatures.RNA$dispersion.cutoff)

      print("Scaling data...")
      seu_wdoublets <- ScaleData(seu_wdoublets,
                                 features = orig.commands$ScaleData.RNA$features,
                                 model.use = orig.commands$ScaleData.RNA$model.use,
                                 do.scale = orig.commands$ScaleData.RNA$do.scale,
                                 do.center = orig.commands$ScaleData.RNA$do.center,
                                 scale.max = orig.commands$ScaleData.RNA$scale.max,
                                 block.size = orig.commands$ScaleData.RNA$block.size,
                                 min.cells.to.block = orig.commands$ScaleData.RNA$min.cells.to.block)

      print("Running PCA...")
      seu_wdoublets <- RunPCA(seu_wdoublets,
                              features = orig.commands$ScaleData.RNA$features,
                              npcs = length(PCs),
                              rev.pca =  orig.commands$RunPCA.RNA$rev.pca,
                              weight.by.var = orig.commands$RunPCA.RNA$weight.by.var,
                              verbose=FALSE)
      pca.coord <- seu_wdoublets@reductions$pca@cell.embeddings[ , PCs]
      cell.names <- rownames(seu_wdoublets@meta.data)
      nCells <- length(cell.names)
      rm(seu_wdoublets); gc() # Free up memory
    }

    if (sct == TRUE) {
      require(sctransform)
      print("Creating Seurat object...")
      seu_wdoublets <- CreateSeuratObject(counts = data_wdoublets)

      print("Running SCTransform...")
      seu_wdoublets <- SCTransform(seu_wdoublets)

      print("Running PCA...")
      seu_wdoublets <- RunPCA(seu_wdoublets, npcs = length(PCs))
      pca.coord <- seu_wdoublets@reductions$pca@cell.embeddings[ , PCs]
      cell.names <- rownames(seu_wdoublets@meta.data)
      nCells <- length(cell.names)
      rm(seu_wdoublets); gc()
    }

    ## Compute PC distance matrix
    print("Calculating PC distance matrix...")
    dist.mat <- fields::rdist(pca.coord)

    ## Compute pANN
    print("Computing pANN...")
    pANN <- as.data.frame(matrix(0L, nrow = n_real.cells, ncol = 1))
    rownames(pANN) <- real.cells
    colnames(pANN) <- "pANN"
    k <- round(nCells * pK)
    for (i in 1:n_real.cells) {
      neighbors <- order(dist.mat[, i])
      neighbors <- neighbors[2:(k + 1)]
      neighbor.names <- rownames(dist.mat)[neighbors]
      pANN$pANN[i] <- length(which(neighbors > n_real.cells))/k
    }

    print("Classifying doublets..")
    classifications <- rep("Singlet",n_real.cells)
    classifications[order(pANN$pANN[1:n_real.cells], decreasing=TRUE)[1:nExp]] <- "Doublet"
    seu@meta.data[, paste("pANN",pN,pK,nExp,sep="_")] <- pANN[rownames(seu@meta.data), 1]
    seu@meta.data[, paste("DF.classifications",pN,pK,nExp,sep="_")] <- classifications
    return(seu)
  }
}
## usage: https://rdrr.io/github/chris-mcginnis-ucsf/DoubletFinder/man/doubletFinder_v3.html
## source: https://github.com/chris-mcginnis-ucsf/DoubletFinder/blob/master/R/doubletFinder_v3.R

Run DoubletFinder

table(pb_sex@meta.data$DF.classifications_0.25_0.01_440)

Doublet Singlet 
    440    6191 

results in:

table(pb_sex@meta.data$DF.classifications_0.25_0.01_440)

Validation and visualisation of doublets

visualise where doublets are:

doublet.cells <- c(rownames(pb_sex@meta.data[pb_sex@meta.data$DF.classifications_0.25_0.01_440 == "Doublet",]))
d1 <- DimPlot(pb_sex, reduction = "umap", cells.highlight = doublet.cells, sizes.highlight = 2)
#TSNEPlot(object = pb_sex, cells.highlight = doublet.cells, do.return = TRUE, )
doublet1 <- d1 + coord_fixed() + theme(axis.text.x=element_blank())

## plot clusters
cluster_plot <- DimPlot(pb_sex, reduction = "umap", group.by = "ident", label = TRUE)

doublet1 + cluster_plot

VlnPlot(object = pb_sex, features = "pANN_0.25_0.01_440", pt.size = 0.1)
## extract meta data cols of interest
df <- pb_sex@meta.data[,c("RNA_snn_res.1","DF.classifications_0.25_0.01_440")]

## make a table from doublets
df <- data.frame(rbind(table(df)))

## ammend rownames
df$cluster <- rownames(df)

## calculate percentages of cells that are doublets
df$pc_doublet <- ((df[,1])/((df[,1]) + df[,2]))*100

## inspect
#kable(df[order(df$pc_doublet),])

## plot 
ggplot(data=df, aes(x=cluster, y=pc_doublet)) +
  geom_col(fill="steelblue") +
  theme_minimal()

Filter doublets

It definitely seems like there are some biases in doublet detection. Fewer doublets in very early rings and in mature sexes may be due to a smaller number of the population being these cells.

It may also be biological, that these cells are less likely to associate to one another (although less likely, as doublets are a result of the probability of two cells being captured inside the same droplet together at a certain loading concentration, rather than two cells already being together upon droplet capture).

remove doublets:

## make a list of singlet cells
keep_singlets <- rownames(pb_sex@meta.data[pb_sex@meta.data$DF.classifications_0.25_0.01_440 == "Singlet",])

## subset into new seurat object
pb_sex_filtered <- subset(pb_sex, cells = keep_singlets, subset.raw = TRUE)
The following arguments are not used: subset.raw
## compare old and new objects
pb_sex
An object of class Seurat 
5098 features across 6631 samples within 1 assay 
Active assay: RNA (5098 features, 2000 variable features)
 2 dimensional reductions calculated: pca, umap
pb_sex_filtered
An object of class Seurat 
5098 features across 6191 samples within 1 assay 
Active assay: RNA (5098 features, 2000 variable features)
 2 dimensional reductions calculated: pca, umap

7. Life Cycle Stage (Using Bulk RNA-Seq Correlation)

Add in bulk data predictions

hoo et al.

#Pb Prediction correlations with bulk data (asexual hoo): 

#Load in required package:
library(Hmisc)
#Cooerce expression data into a matrix and load in the reference timecourse data:
x10 <- as.matrix(pb_sex_filtered@assays$RNA@data)
rownames(x10) <- gsub("-", "_", rownames(x10))
#read in bulk data:
hoo<-as.matrix(read.table("/Users/Andy/GCSKO/GCSKO_analysis_git/data/Reference/hoo_berg2.txt",header=T, row.names=1))
#Make a blank dataframe in which to add prediction:
df <- data.frame(matrix(ncol = 4, nrow = 0))
colnames(df) <- c("Prediction(Spearman)","r(Spearman)","Prediction(Pearsons)","r(Pearsons)")
#Do correlations with bulk data using both Spearman and Pearson (and the top 1000 genes):
for (i in 1:ncol(x10))
{
  shared<-intersect(row.names(as.matrix(head(sort(x10[,i], decreasing=TRUE),1000))),row.names(hoo))
  step0<-rcorr(x10[shared,i],hoo[shared,1:12],type = "spearman")
  step1<-as.matrix(t(step0$r[2:13,1]))
  step2<-rcorr(x10[shared,i],hoo[shared,1:12],type = "pearson")
  step3<-as.matrix(t(step2$r[2:13,1]))
  step4<-cbind(colnames(step1)[which.max(step1)],step1[which.max(step1)],colnames(step3)[which.max(step3)],step3[which.max(step3)])
  colnames(step4) <- c("Prediction(Spearman)","r(Spearman)","Prediction(Pearsons)","r(Pearsons)")
  rownames(step4)<-colnames(x10)[i]
  df<-rbind(df,step4)
}
#Write out data into a csv file:
#write.csv(dfringr,file="/Users/ar19/Desktop/PhD/AR04_GCSKO_project/All_mutants_Feb_2018/predictionpbcombined.csv")
#Change the format of the output to make it more readable:
#gsub("Pb_","", dfringr[,1]) - Make predictions into 18hr.dat format:

#spearman:
df[,1] <- gsub("Pb_","", df[,1])
#Remove hr.dat from list:
df[,1] <- gsub("hr.dat","", df[,1])
#Check - dfringr[,1]
#Make into a number:
df[,1] <- as.numeric(df[,1])
df[,2] <- as.numeric(as.character(df[,2]))

#pearson:
df[,3] <- gsub("Pb_","", df[,3])
#Remove hr.dat from list:
df[,3] <- gsub("hr.dat","", df[,3])
#Check - dfringr[,1]
#Make into a number:
df[,3] <- as.numeric(df[,3])
df[,4] <- as.numeric(as.character(df[,4]))
#add to 10X object:
pb_sex_filtered <- AddMetaData(pb_sex_filtered, metadata = df)
Invalid name supplied, making object name syntactically valid. New object name is Prediction.Spearman.r.Spearman.Prediction.Pearsons.r.Pearsons.; see ?make.names for more details on syntax validity

Kasia’s data

Can also do with Kasia’s timecourse data:

kas<-as.matrix(read.table("/Users/Andy/GCSKO/GCSKO_analysis_git/data/Reference/AP2OETC.txt",header=T, row.names=1))
#Make a blank dataframe in which to add prediction:
dfs <- data.frame(matrix(ncol = 4, nrow = 0))
colnames(dfs) <- c("ID","Prediction","r (Pearson)")
#Do correlations with bulk data using both Spearman and Pearson (and the top 1000 genes):
for (i in 1:ncol(x10))
{
  shared<-intersect(row.names(as.matrix(head(sort(x10[,i], decreasing=TRUE),1000))),rownames(kas))
  step0<-rcorr(x10[shared,i],kas[shared,1:10],type = "spearman")
  step1<-as.matrix(t(step0$r[2:11,1]))
  step2<-rcorr(x10[shared,i],kas[shared,1:10],type = "pearson")
  step3<-as.matrix(t(step2$r[2:11,1]))
  step4<-cbind(colnames(step1)[which.max(step1)],step1[which.max(step1)],colnames(step3)[which.max(step3)],step3[which.max(step3)])
  colnames(step4) <- c("Prediction(Spearman)","r(Spearman)","Prediction(Pearsons)","r(Pearsons)")
  rownames(step4)<-colnames(x10)[i]
  dfs<-rbind(dfs,step4)
}
#Write out data into a csv file:
#write.csv(df,file="/Users/ar19/Desktop/PhD/AR04_GCSKO_project/All_mutants_Feb_2018/predictionkasiacombined.csv")

#Change the format of the output to make it more readable:
#gsub("Pb_","", dfs[,1]) - Make predictions into 18hr.dat format:
dfs[,1] <- gsub("X","", dfs[,1])
#Make into a number:
dfs[,1] <- as.numeric(dfs[,1])
#Make into a number:
dfs[,2] <- as.numeric(as.character(dfs[,2]))

#gsub("Pb_","", dfs[,1]) - Make predictions into 18hr.dat format:
dfs[,3] <- gsub("X","", dfs[,3])
#Make into a number:
dfs[,3] <- as.numeric(dfs[,3])
#dfs[,1]
#Make into a number:
dfs[,4] <- as.numeric(as.character(dfs[,4]))

colnames(dfs) <- c('Prediction(Spearman)_Kasia', 'r(Spearman)_Kasia', 'Prediction(Pearson)_Kasia', 'r(Pearson)_Kasia')
#add to Seurat:
#add to 10X object:
pb_sex_filtered <- AddMetaData(pb_sex_filtered, dfs)
Invalid name supplied, making object name syntactically valid. New object name is Prediction.Spearman._Kasiar.Spearman._KasiaPrediction.Pearson._Kasiar.Pearson._Kasia; see ?make.names for more details on syntax validity

Visualise

Confirm life cycle designations:

## plot
FeaturePlot(pb_sex_filtered, features = c("Prediction.Spearman._Kasia", "Prediction.Spearman."))

optimse UMAP

## PCA calc
pb_sex_filtered <- RunPCA(pb_sex_filtered, features = VariableFeatures(object = pb_sex_filtered))
The following 5 features requested have zero variance (running reduction without them): PBANKA-0612861, PBANKA-0836921, PBANKA-1000041, PBANKA-API0290, PBANKA-API0031PC_ 1 
Positive:  PBANKA-1235600, PBANKA-0931300, PBANKA-1214300, PBANKA-1400400, PBANKA-1101100, PBANKA-0205000, PBANKA-0311800, PBANKA-0823400, PBANKA-1242300, PBANKA-1246600 
       PBANKA-0722600, PBANKA-1008500, PBANKA-0932400, PBANKA-1308600, PBANKA-1314800, PBANKA-1309900, PBANKA-1300096, PBANKA-1145400, PBANKA-1200096, PBANKA-1100441 
       PBANKA-0722801, PBANKA-1101300, PBANKA-1326400, PBANKA-1040521, PBANKA-0941300, PBANKA-1127000, PBANKA-1303800, PBANKA-0406500, PBANKA-1210200, PBANKA-0814200 
Negative:  PBANKA-1312700, PBANKA-1224900, PBANKA-0824400, PBANKA-1115200, PBANKA-1453000, PBANKA-1432200, PBANKA-1204200, PBANKA-0703500, PBANKA-0702000, PBANKA-0812600 
       PBANKA-0312700, PBANKA-1128800, PBANKA-1218300, PBANKA-1232600, PBANKA-1432400, PBANKA-1430600, PBANKA-1449300, PBANKA-1106500, PBANKA-0209300, PBANKA-0620400 
       PBANKA-1421500, PBANKA-1336200, PBANKA-1320800, PBANKA-0417600, PBANKA-1038800, PBANKA-1129800, PBANKA-1414500, PBANKA-1451100, PBANKA-1143800, PBANKA-0402700 
PC_ 2 
Positive:  PBANKA-1431500, PBANKA-1431400, PBANKA-0942400, PBANKA-1340400, PBANKA-1449000, PBANKA-1038700, PBANKA-0942300, PBANKA-0934700, PBANKA-1208800, PBANKA-0828900 
       PBANKA-0911000, PBANKA-0306100, PBANKA-0927700, PBANKA-0806800, PBANKA-1409600, PBANKA-0406200, PBANKA-0411300, PBANKA-0415800, PBANKA-0507800, PBANKA-0416100 
       PBANKA-1033700, PBANKA-1361500, PBANKA-1108800, PBANKA-0920700, PBANKA-0510600, PBANKA-0925400, PBANKA-0720100, PBANKA-0507300, PBANKA-1119800, PBANKA-1030400 
Negative:  PBANKA-1317200, PBANKA-1352500, PBANKA-0827400, PBANKA-0810700, PBANKA-1225500, PBANKA-1319500, PBANKA-0402500, PBANKA-1419300, PBANKA-0714000, PBANKA-1315300 
       PBANKA-1436300, PBANKA-0105100, PBANKA-1311400, PBANKA-0821400, PBANKA-0417600, PBANKA-0204500, PBANKA-1342300, PBANKA-0417200, PBANKA-0605800, PBANKA-0704900 
       PBANKA-0517600, PBANKA-1231300, PBANKA-1329900, PBANKA-1415200, PBANKA-1115000, PBANKA-1134900, PBANKA-1035200, PBANKA-1233600, PBANKA-0907100, PBANKA-0912500 
PC_ 3 
Positive:  PBANKA-1400600, PBANKA-1459300, PBANKA-0416000, PBANKA-0305100, PBANKA-0304800, PBANKA-1443300, PBANKA-1365200, PBANKA-0830200, PBANKA-0938400, PBANKA-1349000 
       PBANKA-0827200, PBANKA-1137800, PBANKA-1437300, PBANKA-1113300, PBANKA-0932200, PBANKA-0506100, PBANKA-1017100, PBANKA-0313800, PBANKA-0408500, PBANKA-1117000 
       PBANKA-0409800, PBANKA-0922500, PBANKA-0519400, PBANKA-0925600, PBANKA-0316600, PBANKA-0915200, PBANKA-0722921, PBANKA-0934300, PBANKA-0941800, PBANKA-1225000 
Negative:  PBANKA-1431500, PBANKA-1431400, PBANKA-1449000, PBANKA-0806800, PBANKA-1108000, PBANKA-0927700, PBANKA-0911000, PBANKA-0828900, PBANKA-0601200, PBANKA-1208800 
       PBANKA-1409600, PBANKA-0830900, PBANKA-0521200, PBANKA-1029400, PBANKA-0102700, PBANKA-0942400, PBANKA-1304500, PBANKA-1316500, PBANKA-1038700, PBANKA-1333100 
       PBANKA-0902400, PBANKA-1109600, PBANKA-1129600, PBANKA-1119800, PBANKA-0822900, PBANKA-0719700, PBANKA-1429100, PBANKA-1038200, PBANKA-0301800, PBANKA-1320100 
PC_ 4 
Positive:  PBANKA-0519000, PBANKA-1349100, PBANKA-0519100, PBANKA-0519300, PBANKA-1344400, PBANKA-0831000, PBANKA-1002400, PBANKA-0713100, PBANKA-0523700, PBANKA-0111000 
       PBANKA-1032100, PBANKA-1014500, PBANKA-0519400, PBANKA-0932000, PBANKA-0804500, PBANKA-1349000, PBANKA-1315700, PBANKA-0501400, PBANKA-1101400, PBANKA-1425900 
       PBANKA-0519200, PBANKA-1210600, PBANKA-0509600, PBANKA-0619700, PBANKA-1327100, PBANKA-0523800, PBANKA-0307500, PBANKA-1035400, PBANKA-1119600, PBANKA-1117200 
Negative:  PBANKA-0107300, PBANKA-0814200, PBANKA-1214300, PBANKA-0713300, PBANKA-0604300, PBANKA-0405200, PBANKA-0932200, PBANKA-0109100, PBANKA-0823400, PBANKA-0938400 
       PBANKA-0416500, PBANKA-1460400, PBANKA-1437300, PBANKA-1302800, PBANKA-1130500, PBANKA-0406500, PBANKA-1448000, PBANKA-0211500, PBANKA-1444100, PBANKA-1450300 
       PBANKA-0710100, PBANKA-1223100, PBANKA-1419100, PBANKA-0313800, PBANKA-0818900, PBANKA-0919600, PBANKA-1235600, PBANKA-1242300, PBANKA-0916200, PBANKA-1203700 
PC_ 5 
Positive:  PBANKA-0501600, PBANKA-1240600, PBANKA-0707700, PBANKA-0112200, PBANKA-1002600, PBANKA-0208900, PBANKA-0812200, PBANKA-1423000, PBANKA-0832800, PBANKA-1409200 
       PBANKA-0307500, PBANKA-0716900, PBANKA-0100400, PBANKA-1002400, PBANKA-0915000, PBANKA-1364400, PBANKA-1119600, PBANKA-1347000, PBANKA-0819600, PBANKA-1319000 
       PBANKA-1212500, PBANKA-0900900, PBANKA-1202000, PBANKA-1400091, PBANKA-1228800, PBANKA-0911700, PBANKA-0311500, PBANKA-1345900, PBANKA-1025300, PBANKA-0519200 
Negative:  PBANKA-1443300, PBANKA-0925600, PBANKA-0306700, PBANKA-0907200, PBANKA-1349000, PBANKA-1106500, PBANKA-1217400, PBANKA-1107600, PBANKA-1319300, PBANKA-0304800 
       PBANKA-0519400, PBANKA-0312500, PBANKA-1313100, PBANKA-0305100, PBANKA-0112100, PBANKA-0611700, PBANKA-1241800, PBANKA-0304400, PBANKA-0833300, PBANKA-0208000 
       PBANKA-1113300, PBANKA-1339300, PBANKA-1438000, PBANKA-0206300, PBANKA-1340500, PBANKA-0207500, PBANKA-0915200, PBANKA-0808000, PBANKA-1020100, PBANKA-1360000 
## elbow plot
ElbowPlot(pb_sex_filtered, ndims = 30, reduction = "pca")


## UMAP calc
pb_sex_filtered <- RunUMAP(pb_sex_filtered, dims = 1:8, seed.use = 300, n.neighbors = 60, min.dist = 0.5, repulsion.strength = 0.05, local.connectivity = 20)
20:39:44 UMAP embedding parameters a = 0.583 b = 1.334
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by ‘BiocGenerics’
20:39:44 Read 6191 rows and found 8 numeric columns
20:39:44 Using Annoy for neighbor search, n_neighbors = 60
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by ‘BiocGenerics’
20:39:44 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
20:39:46 Writing NN index file to temp file /var/folders/wj/rztzclxn1t10cl2sk0plbf3r0000gn/T//RtmpXGhNV1/file43376acfbe03
20:39:46 Searching Annoy index using 1 thread, search_k = 6000
20:39:51 Annoy recall = 100%
20:39:53 Commencing smooth kNN distance calibration using 1 thread
20:39:54 6191 smooth knn distance failures
20:39:57 Initializing from normalized Laplacian + noise
20:39:57 Commencing optimization for 500 epochs, with 184004 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
20:40:27 Optimization finished
## UMAP plot
DimPlot(pb_sex_filtered, reduction = "umap", group.by = "ident", label = TRUE)

8. Save and Export

Save environment

## This saves everything in the global environment for easy recall later
#save.image(file = "GCSKO_10X_QC.RData")
#load(file = "GCSKO_10X_QC.RData")

Save object(s)

## save Rdata
#save(pb_30k_sex_filtered, pb_sex_filtered, file = "Part_2_input.Rdata")
#load(file = "Part_2_input.Rdata")

## save RDS
saveRDS(pb_sex_filtered, file = "/Users/Andy/GCSKO/GCSKO_analysis_git/data_to_export/pb_sex_filtered.RDS", compress = FALSE) 
#pb_sex_filtered <- readRDS("pb_sex_filtered.RDS")

## Save Robj
#save(pb_sex_filtered,file="pb_sex_filtered.Robj")

Appendix

Session Info

LS0tCnN1YnRpdGxlOiAnR2FtZXRvY3l0ZSBEZXZlbG9wbWVudCBpbiA8aT5QbGFzbW9kaXVtIGJlcmdoZWk8L2k+Jwp0aXRsZTogfAogICFbXSguLi9HQ1NLT19sb2dvLmpwZyl7d2lkdGg9MzAwcHh9ICAKICAxMFggUXVhbGl0eSBDb250cm9sCmF1dGhvcjogIltBbmRyZXcgUnVzc2VsbF0oaHR0cHM6Ly9hamNydXNzZWxsLndpeHNpdGUuY29tL215c2l0ZS9hYm91dCkiCmluc3RpdHV0ZTogV2VsbGNvbWUgU2FuZ2VyIEluc3RpdHV0ZQpkYXRlOiAnYHIgZm9ybWF0KFN5cy5EYXRlKCksICIlQiAlZCwgJVkiKWAnCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IGNvc21vCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICAjdG9jX2Zsb2F0OiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KKioqCiMgMS4gSW50cm9kdWN0aW9uIGFuZCBBaW1zIHsudGFic2V0fQoKVHdvIGRhdGFzZXRzIHdlcmUgZ2VuZXJhdGVkIHVzaW5nIHRoZSAxMFggR2Vub21pY3MgQ2hyb21pdW0gMycgc2NSTkEtU2VxIHBsYXRmb3JtOgoKYGBge3IgaW50cm8sIGVjaG89RkFMU0UsIHJlc3VsdHM9J2FzaXMnfQojIyBsb2FkIGtuaXRyIHRvIGRpc3BsYXkgdGFibGUKbGlicmFyeShrbml0cikKIyMgbWFrZSBkYXRhZnJhbWUKc3BlY2llcyA8LSBjKCJwYiIsICJwYiIpCmV4cGVyaW1lbnRfbmFtZSA8LSBjKCdzdHJhaWdodCBibGVlZCBleHBlcmltZW50JywnMToxIG1peCBleHBlcmltZW50JykKcnVuX251bWJlciA8LSBjKCIyMjI1MiIsICIyNDI4NCIpCmxhbmVfbnVtYmVyIDwtIGMoIjUiLCAiMSAmIDIiKQpzZXF1ZW5jZXIgPC0gYygiSGlzZXEgNDAwMCIsICJIaXNlcSAyNTAwIikKYXBwcm94aW1hdGVfbnVtYmVyX29mX2NlbGxzIDwtIGMoIjMwLDAwMCIsICI1LDAwMCIpCmVtcGxveS5kYXRhIDwtIGRhdGEuZnJhbWUoc3BlY2llcywgZXhwZXJpbWVudF9uYW1lLCBydW5fbnVtYmVyLCBsYW5lX251bWJlciwgc2VxdWVuY2VyLCBhcHByb3hpbWF0ZV9udW1iZXJfb2ZfY2VsbHMsIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpCiMjIHByaW50IGRhdGFmcmFtZQprYWJsZShlbXBsb3kuZGF0YSkKYGBgCgpUaGlzIHNjcmlwdCBkZXRhaWxzIHRoZSBxdWFsaXR5IGNvbnRyb2wgb2YgdGhlIDE6MSBtaXggZXhwZXJpbWVudAoKVGhpcyBkYXRhIGhhcyBiZWVuIHByb2Nlc3NlZCB1c2luZyBDZWxsUmFuZ2VyIGludG8gY291bnRzIHRhYmxlcy4gVGhpcyBpbml0aWFsIGFuYWx5c2lzIGdhdmUgdGhlIGZvbGxvd2luZyBtZXRyaWNzOgoKPGI+UGIgMToxIG1peCBleHBlcmltZW50IChydW4gIzogMjQyODQgbGFuZXMgMSBhbmQgMiAoSGlzZXEgMjUwMCkpOjwvYj4KCjxkaXYgc3R5bGU9IndpZHRoOjUwMHB4OyBoZWlnaHQ6NDAwcHgiPiFbXSguLi9kYXRhLzEwWC8yNDI4NF9jZWxscmFuZ2VyX291dHB1dF8xLnBuZyk8L2Rpdj4KPGRpdiBzdHlsZT0id2lkdGg6NTAwcHg7IGhlaWdodDoyMDBweCI+IVtdKC4uL2RhdGEvMTBYLzI0Mjg0X2NlbGxyYW5nZXJfb3V0cHV0XzIucG5nKTwvZGl2PgoKPGI+V2Ugd2lsbCBsb2FkIHRoaXMgZGF0YSBpbiBhbmQgZm9yIGVhY2ggcnVuOjwvYj4KCkEuIERlZmluZSAnY2VsbHMnCgpCLiBGaWx0ZXIgcG9vciBxdWFsaXR5IGNlbGxzIG91dAoKQy4gRGltZW5zaW9uYWxpdHkgUmVkdWN0aW9uIGFuZCBDbHVzdGVyaW5nIAoKRC4gUmVtb3ZlIERvdWJsZXRzCgpFLiBQcmVkaWN0IGxpZmUgQ3ljbGUgU3RhZ2UgKFVzaW5nIEJ1bGsgUk5BLVNlcSBDb3JyZWxhdGlvbikKCiMgMi4gUmVhZCBpbiB0aGUgZGF0YSAgey50YWJzZXR9CgojIyMgTG9hZCB0aGUgcmVxdWlyZWQgcGFja2FnZXMKCmBgYHtyIGxvYWQgcGFja2FnZXMsIGVjaG8gPSBGQUxTRX0KIyMgU2V1cmF0IGlzIG5lZWRlZCBmb3IgbW9zdCBvZiB0aGlzIHNjcmlwdAppZihyZXF1aXJlKCJTZXVyYXQiLCBxdWlldGx5ID0gVFJVRSkpewogICAgcHJpbnQoIlNldXJhdCBpcyBsb2FkZWQgY29ycmVjdGx5IikKfSBlbHNlIHsKICAgIHByaW50KCJ0cnlpbmcgdG8gaW5zdGFsbCBTZXVyYXQiKQogICAgaW5zdGFsbC5wYWNrYWdlcygiU2V1cmF0IikKICAgIGlmKHJlcXVpcmUoU2V1cmF0KSl7CiAgICAgICAgcHJpbnQoIlNldXJhdCBpbnN0YWxsZWQgYW5kIGxvYWRlZCIpCiAgICB9IGVsc2UgewogICAgICAgIHN0b3AoImNvdWxkIG5vdCBpbnN0YWxsIFNldXJhdCIpCiAgICB9Cn0KCiMjIGNvd3Bsb3QgaXMgbmVlZGVkIGZvciBwbG90cwppZihyZXF1aXJlKCJjb3dwbG90IikpewogICAgcHJpbnQoImNvd3Bsb3QgaXMgbG9hZGVkIGNvcnJlY3RseSIpCn0gZWxzZSB7CiAgICBwcmludCgidHJ5aW5nIHRvIGluc3RhbGwgY293cGxvdCIpCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJjb3dwbG90IikKICAgIGlmKHJlcXVpcmUoY293cGxvdCkpewogICAgICAgIHByaW50KCJjb3dwbG90IGluc3RhbGxlZCBhbmQgbG9hZGVkIikKICAgIH0gZWxzZSB7CiAgICAgICAgc3RvcCgiY291bGQgbm90IGluc3RhbGwgY293cGxvdCIpCiAgICB9Cn0KCiMjIGdyaWRFeHRyYSBpcyBuZWVkZWQgZm9yIGdyaWQgZ3JhcGhpY3MgdG8gcGxvdCBtdWx0aXBsZSBwbG90cyBpbiB0aGUgc2FtZSB2aWV3CmlmKHJlcXVpcmUoImdyaWRFeHRyYSIpKXsKICAgIHByaW50KCJncmlkRXh0cmEgaXMgbG9hZGVkIGNvcnJlY3RseSIpCn0gZWxzZSB7CiAgICBwcmludCgidHJ5aW5nIHRvIGluc3RhbGwgZ3JpZEV4dHJhIikKICAgIGluc3RhbGwucGFja2FnZXMoImdyaWRFeHRyYSIpCiAgICBpZihyZXF1aXJlKGdyaWRFeHRyYSkpewogICAgICAgIHByaW50KCJncmlkRXh0cmEgaW5zdGFsbGVkIGFuZCBsb2FkZWQiKQogICAgfSBlbHNlIHsKICAgICAgICBzdG9wKCJjb3VsZCBub3QgaW5zdGFsbCBncmlkRXh0cmEiKQogICAgfQp9CgojI2ZvciBncmlkLmFycmFuZ2UgZnVuY3Rpb24gdG8gY2hhbmdlIHNpemUgb2YgdGl0bGUKaWYocmVxdWlyZSgiZ3JpZCIpKXsKICAgIHByaW50KCJncmlkIGlzIGxvYWRlZCBjb3JyZWN0bHkiKQp9IGVsc2UgewogICAgcHJpbnQoInRyeWluZyB0byBpbnN0YWxsIGdyaWQiKQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ3JpZCIpCiAgICBpZihyZXF1aXJlKGdyaWQpKXsKICAgICAgICBwcmludCgiZ3JpZCBpbnN0YWxsZWQgYW5kIGxvYWRlZCIpCiAgICB9IGVsc2UgewogICAgICAgIHN0b3AoImNvdWxkIG5vdCBpbnN0YWxsIGdyaWQiKQogICAgfQp9CgojIyBmb3IgZG9pbmcgYnVsayBjb3JyZWxhdGlvbiBjYWxjdWxhdGlvbnMKaWYocmVxdWlyZSgiSG1pc2MiKSl7CiAgICBwcmludCgiSG1pc2MgaXMgbG9hZGVkIGNvcnJlY3RseSIpCn0gZWxzZSB7CiAgICBwcmludCgidHJ5aW5nIHRvIGluc3RhbGwgSG1pc2MiKQogICAgaW5zdGFsbC5wYWNrYWdlcygiSG1pc2MiKQogICAgaWYocmVxdWlyZShIbWlzYykpewogICAgICAgIHByaW50KCJIbWlzYyBpbnN0YWxsZWQgYW5kIGxvYWRlZCIpCiAgICB9IGVsc2UgewogICAgICAgIHN0b3AoImNvdWxkIG5vdCBpbnN0YWxsIEhtaXNjIikKICAgIH0KfQoKIyMgZHBseXIgaXMgbmVlZGVkIHRvIHdvcmsgd2l0aCBkYXRhIGZyYW1lcwppZihyZXF1aXJlKCJkcGx5ciIpKXsKICAgIHByaW50KCJkcGx5ciBpcyBsb2FkZWQgY29ycmVjdGx5IikKfSBlbHNlIHsKICAgIHByaW50KCJ0cnlpbmcgdG8gaW5zdGFsbCBkcGx5ciIpCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpCiAgICBpZihyZXF1aXJlKGRwbHlyKSl7CiAgICAgICAgcHJpbnQoImRwbHlyIGluc3RhbGxlZCBhbmQgbG9hZGVkIikKICAgIH0gZWxzZSB7CiAgICAgICAgc3RvcCgiY291bGQgbm90IGluc3RhbGwgZHBseXIiKQogICAgfQp9CgojIyBzY2FsZXMgaXMgbmVlZGVkIGZvciBicmVhayBmb3JtYXR0aW5nIGZ1bmN0aW9ucyBpbiB0aGUgYmFyY29kZSBwbG90CmlmKHJlcXVpcmUoInNjYWxlcyIpKXsKICAgIHByaW50KCJzY2FsZXMgaXMgbG9hZGVkIGNvcnJlY3RseSIpCn0gZWxzZSB7CiAgICBwcmludCgidHJ5aW5nIHRvIGluc3RhbGwgc2NhbGVzIikKICAgIGluc3RhbGwucGFja2FnZXMoInNjYWxlcyIpCiAgICBpZihyZXF1aXJlKHNjYWxlcykpewogICAgICAgIHByaW50KCJzY2FsZXMgaW5zdGFsbGVkIGFuZCBsb2FkZWQiKQogICAgfSBlbHNlIHsKICAgICAgICBzdG9wKCJjb3VsZCBub3QgaW5zdGFsbCBzY2FsZXMiKQogICAgfQp9CgojIyBnZ3B1YnIgaXMgbmVlZGVkIGZvciBwbG90dGluZwppZihyZXF1aXJlKCJnZ3B1YnIiLCBxdWlldGx5ID0gVFJVRSkpewogICAgcHJpbnQoImdncHViciBpcyBsb2FkZWQgY29ycmVjdGx5IikKfSBlbHNlIHsKICAgIHByaW50KCJ0cnlpbmcgdG8gaW5zdGFsbCBnZ3B1YnIiKQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwdWJyIikKICAgIGlmKHJlcXVpcmUoZ2dwdWJyKSl7CiAgICAgICAgcHJpbnQoImdncHViciBpbnN0YWxsZWQgYW5kIGxvYWRlZCIpCiAgICB9IGVsc2UgewogICAgICAgIHN0b3AoImNvdWxkIG5vdCBpbnN0YWxsIGdncHViciIpCiAgICB9Cn0KCiMjIHBhdGNod29yayBpcyBuZWVkZWQgZm9yIHBsb3R0aW5nCiMjIFdBUk5JTkchIGNvd3Bsb3Qgb3Zlci1yaWRlcyB0aGlzIGJ5IG1hc2tpbmcgaXQgc28gYmUgY2FyZWZ1bC4KaWYocmVxdWlyZSgicGF0Y2h3b3JrIiwgcXVpZXRseSA9IFRSVUUpKXsKICAgIHByaW50KCJwYXRjaHdvcmsgaXMgbG9hZGVkIGNvcnJlY3RseSIpCn0gZWxzZSB7CiAgICBwcmludCgidHJ5aW5nIHRvIGluc3RhbGwgcGF0Y2h3b3JrIikKICAgIGluc3RhbGwucGFja2FnZXMoInBhdGNod29yayIpCiAgICBpZihyZXF1aXJlKHBhdGNod29yaykpewogICAgICAgIHByaW50KCJwYXRjaHdvcmsgaW5zdGFsbGVkIGFuZCBsb2FkZWQiKQogICAgfSBlbHNlIHsKICAgICAgICBzdG9wKCJjb3VsZCBub3QgaW5zdGFsbCBwYXRjaHdvcmsiKQogICAgfQp9CgojIyBzZXQgdGhlIHNlZWQgZm9yIGJvdGggdGhlIG1peHR1cmUgbW9kZWxzIGFuZCBhbHNvIGZvciB0aGUgc2FtcGxlIGZ1bmN0aW9uIGxhdGVyIG9uOgpzZXQuc2VlZCgtOTI0OTcpCmBgYAoKIyMjIEltcG9ydCBHVEYgZmlsZQoKVGhpcyB3aWxsIGJlIGhlbHBmdWwgbGF0ZXIgb24uIFRoaXMgY29udGFpbnMgYW5ub3RhdGlvbnMgZm9yIGVhY2ggZ2VuZToKYGBge3IgaW1wb3J0IGd0Zn0KIyNJbXBvcnQgZ3RmIGZpbGU6Cmd0ZiA8LSByZWFkLnRhYmxlKCIuLi9kYXRhL1JlZmVyZW5jZS9QYmVyZ2hlaS5ndGYiLCBzZXA9Ilx0IiwgaGVhZGVyID0gRkFMU0UpCmhlYWQoZ3RmKQpgYGAKCiMjIyBSZWFkIGluIHRoZSBEYXRhCgpgYGB7ciBpbXBvcnQgZGF0YX0KIyMgcmVhZCBpbiAxMHggb3V0cHV0IAp0ZW54NWtfcmF3X2RhdGEgPC0gUmVhZDEwWCgiLi4vZGF0YS8xMFgvdGVueF8yNDI4NCIpCgojIyBDcmVhdGUgU2V1cmF0IG9iamVjdAp0ZW54NWsgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IHRlbng1a19yYXdfZGF0YSwgbWluLmNlbGxzID0gMCwgbWluLmZlYXR1cmVzID0gMCwgcHJvamVjdCA9ICJHQ1NLTyIpCgojIyBhZGQgZXhwZXJpbWVudCB0byBtZXRhIGRhdGEgZm9yIG1lcmdpbmcgbGF0ZXIKdGVueDVrQG1ldGEuZGF0YSRleHBlcmltZW50IDwtICJ0ZW54NWsiCgojIyBpbnNwZWN0CnRlbng1awpgYGAKCiMgMy4gRGVmaW5pbmcgQ2VsbHMgdnMuIEJhY2tncm91bmQgey50YWJzZXR9CgpQbG90IGEga25lZSBwbG90IGFuZCB0aGVuIHVzZSBhIG1peHR1cmUgbW9kZWwgdG8gZGVmaW5lIHdoZXJlIHRoZSBjZWxscyB2cy4gYmFja2dyb3VuZCBsaWUKYGBge3IgbWl4bW9kZWwgc2V0dXB9CiMjIGludGVyZXN0aW5nIHJlZmVyZW5jZSBtYXRlcmlhbCBmb3IgdGhpcyBzZWN0aW9uIGNhbiBiZSBmb3VuZCBoZXJlOiBodHRwczovL2hlbWJlcmctbGFiLmdpdGh1Yi5pby9zY1JOQS5zZXEuY291cnNlL3Byb2Nlc3NpbmctcmF3LXNjcm5hLXNlcS1kYXRhLmh0bWwgCgojIyBnZXQgdGhlIG5VTUlzCnVtaV9wZXJfYmFyY29kZSA8LSBhcy5kYXRhLmZyYW1lKHRlbng1a0BtZXRhLmRhdGEkbkNvdW50X1JOQSkKCiMjIHJlbW92ZSB6ZXJvcyBhcyB0aGVzZSBoYXZlIGlzc3VlcyB3aGVuIHlvdSBsb2cgdGhlbSBhbmQgbWFrZSB0aGUgbW9kZWwgbGF0ZXI6CnVtaV9wZXJfYmFyY29kZSA8LSBhcy5kYXRhLmZyYW1lKHVtaV9wZXJfYmFyY29kZVshKHVtaV9wZXJfYmFyY29kZSRgdGVueDVrQG1ldGEuZGF0YSRuQ291bnRfUk5BYD09MCksIF0pCgojIyBnZXQgYSByYW5rIGZvciBlYWNoIGJhcmNvZGUKYmFyY29kZV9yYW5rIDwtIHJhbmsoLXVtaV9wZXJfYmFyY29kZVssMV0pCgojIyB0aGVuIG1ha2UgaW50byBhIGxpc3QKbGliX3NpemUgPC0gKHVtaV9wZXJfYmFyY29kZVssMV0pCgojIyB0aGVuIGxvZyB0aGlzCmxvZ19saWJfc2l6ZSA8LSBsb2cxMCh1bWlfcGVyX2JhcmNvZGVbLDFdKQoKIyNwbG90CiNwbG90KGJhcmNvZGVfcmFuaywgbG9nX2xpYl9zaXplLCB4bGltPWMoMSwxMDAwMDApKQoKIyMgb3JkZXIgdGhlIGJhcmNvZGUgcmFua3MKbyA8LSBvcmRlcihiYXJjb2RlX3JhbmspCgojIyByZW9yZGVyIHRoZSBsaWJyYXJ5IHNpemUsIGJhcmNvZGUgcmFuayBieSB0aGVpciByYW5rCmxvZ19saWJfc2l6ZSA8LSBsb2dfbGliX3NpemVbb10KYmFyY29kZV9yYW5rIDwtIGJhcmNvZGVfcmFua1tvXQpsaWJfc2l6ZSA8LSBsaWJfc2l6ZVtvXQpgYGAKCm1ha2UgYSBtaXh0dXJlIG1vZGVsIHRvIGRldGVybWluZSB0aGUga25lZSBvZiB0aGUgcGxvdApgYGB7ciBtaXhtb2RlbCBjYWxjfQojIyBzZXQgYSBzZWVkIGZvciB0aGUgbWl4dHVyZSBtb2RlbApzZXQuc2VlZCgtOTI0OTcpCgojIyBtaXh0dXJlIG1vZGVsIGNhbGN1bGF0aW9uIApyZXF1aXJlKCJtaXh0b29scyIpCm1peCA8LSBub3JtYWxtaXhFTShsb2dfbGliX3NpemUpCgojIyBwbG90IHJlc3VsdApwbG90KG1peCwgd2hpY2g9MiwgeGxhYjI9ImxvZyhtb2wgcGVyIGNlbGwpIikKYGBgCgpGaW5kIHdoZXJlIHRoZSBkaXN0cmlidXRpb25zIGludGVyc2VjdCAoaS5lLiB3aGVyZSBjZWxscyB2cy4gYmFja2dyb3VuZCBpcykKYGBge3IgY2FsY3VsYXRlIHNwbGl0fQojIyBpZGVudGlmeSB3aGVyZSB0aGUgc3BsaXQgYmV0d2VlbiB0aGUgZGlzdHJpYnV0aW9ucyBpcwpwMSA8LSBkbm9ybShsb2dfbGliX3NpemUsIG1lYW49bWl4JG11WzFdLCBzZD1taXgkc2lnbWFbMV0pCnAyIDwtIGRub3JtKGxvZ19saWJfc2l6ZSwgbWVhbj1taXgkbXVbMl0sIHNkPW1peCRzaWdtYVsyXSkKaWYgKG1peCRtdVsxXSA8IG1peCRtdVsyXSkgewogICAgc3BsaXQgPC0gbWluKGxvZ19saWJfc2l6ZVtwMiA+IHAxXSkKfSBlbHNlIHsKICAgIHNwbGl0IDwtIG1pbihsb2dfbGliX3NpemVbcDEgPiBwMl0pCn0KCiMjIHByaW50IHNwbGl0CnNwbGl0CmBgYAoKVmlldyB0aGUgaW5pdGlhbCByZXN1bHQKYGBge3J9CiMjIGxvZyB0aGUgYmFyY29kZSByYW5rCmxvZ19iYXJjb2RlX3JhbmsgPC0gbG9nMTAoYmFyY29kZV9yYW5rKQoKIyMgcGxvdApwbG90KGxvZ19iYXJjb2RlX3JhbmssIGxvZ19saWJfc2l6ZSwgeGxpbT1jKDEsNikpCiMjIGFkZCB0aGUgc3BsaXQgYXMgYSBsaW5lIG9uIHRoZSBwbG90CmFibGluZShoPXNwbGl0LCBjb2w9InJlZCIpCmBgYAoKRmluYWwgRmlndXJlczoKYGBge3IgYmFyY29kZSBwbG90fQojIyBtYWtlIHRoZSByZXN1bHRzIG9mIHRoZSBhYm92ZSBmdW5jdGlvbnMgaW50byBhIGRhdGFmcmFtZQpkZl9iYXJjb2RlcyA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKGJhcmNvZGVfcmFuaywgbG9nX2xpYl9zaXplLCBsaWJfc2l6ZSksIHJvdy5uYW1lcyA9IE5VTEwpCgojIyBhZGQgYSBjb2x1bW4gZm9yIGlmIGl0IGlzIGEgY2VsbCBvciBub3QKZGZfYmFyY29kZXMkY2VsbCA9IHJvd25hbWVzKGRmX2JhcmNvZGVzKSAlaW4lIHdoaWNoKGRmX2JhcmNvZGVzJGxvZ19saWJfc2l6ZSA+IHNwbGl0KQoKIyMgY2hhbmdlIHZhbHVlIHRvIGEgbnVtZXJpYwpkZl9iYXJjb2RlcyRjZWxsIDwtIGFzLm51bWVyaWMoZGZfYmFyY29kZXMkY2VsbCkKCiMjIGNoYW5nZSB0aGUgMCB0byBhIDIgZm9yIGVhc2Ugb2YgaGFuZGxpbmcKZGZfYmFyY29kZXMkY2VsbFtkZl9iYXJjb2RlcyRjZWxsPDFdIDwtIDIKCiMjIHJlbmFtZSB0aGUgbnVtZXJpY3MgaW50byBjZWxscyBvciBiYWNrZ3JvdW5kCmRmX2JhcmNvZGVzJGNlbGxbZGZfYmFyY29kZXMkY2VsbCA9PSAxXSA8LSAiQ2VsbHMiCmRmX2JhcmNvZGVzJGNlbGxbZGZfYmFyY29kZXMkY2VsbCA9PSAyXSA8LSAiQmFja2dyb3VuZCIKCiMjIGV4dHJhY3QgdGhlIGN1dG9mZiBmb3IgY2VsbHMgZG8geW91IGNhbiBwbG90IHRoZSBsaW5lcwpib3VuZGFyeSA8LSBhcy5udW1lcmljKHN1bShkZl9iYXJjb2RlcyRjZWxsID09ICJDZWxscyIpKQpzcGxpdCA8LSAxMF5zcGxpdAoKIyMgbWFrZSB0aGUgcGxvdApiYXJjb2RlX3Bsb3QgPC0gZ2dwbG90KGRmX2JhcmNvZGVzLCBhZXMoeD1iYXJjb2RlX3JhbmssIHk9bGliX3NpemUsIGNvbG91ciA9IGNlbGwsIHRoZW1lX3NpemUgPSA0MCkpICsKICAjIyBtYWtlIGludG8gYSBkb3QgcGxvdAogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIHNoYXBlID0gMTYpICsKICAjIyBtYWtlIHRoZSBheGlzIGludG8gbG9nIGFuZCBzcGVjaWZ5IGJyZWFrcwogIHNjYWxlX3hfbG9nMTAoYnJlYWtzID0gdHJhbnNfYnJlYWtzKCJsb2cxMCIsIGZ1bmN0aW9uKHgpIDEwXngpLCBsYWJlbHMgPSB0cmFuc19mb3JtYXQoImxvZzEwIiwgbWF0aF9mb3JtYXQoMTBeLngpKSkgKwogICMjIG1ha2UgdGhlIGF4aXMgaW50byBsb2cgYW5kIHNwZWNpZnkgYnJlYWtzCiAgc2NhbGVfeV9sb2cxMChicmVha3MgPSB0cmFuc19icmVha3MoImxvZzEwIiwgZnVuY3Rpb24oeCkgMTBeeCksIGxhYmVscyA9IHRyYW5zX2Zvcm1hdCgibG9nMTAiLCBtYXRoX2Zvcm1hdCgxMF4ueCkpKSArCiAgYW5ub3RhdGlvbl9sb2d0aWNrcygpICsKICAjIyBjaGFuZ2UgY29sb3VycyBvZiBwbG90CiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjYmRiZGJkIiwgIiM1YmE0M2EiKSwgbGFiZWxzID0gYygiQmFja2dyb3VuZCIsICJDZWxscyIpKSArCiAgIyMgY2hhbmdlIGFlcyBvZiBsZWdlbmQKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIsIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yNSksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTI1KSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTI1KSkgKwogICMjIGFkZCB0aGUgbGluZXMgb24gdGhlIHBsb3QKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gc3BsaXQsIHhlbmQgPSBib3VuZGFyeSwgeWVuZCA9IHNwbGl0KSwgY29sb3VyID0gImJsYWNrIiwgYWxwaGEgPSAwLjAxKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gYm91bmRhcnksIHkgPSAwLCB4ZW5kID0gYm91bmRhcnksIHllbmQgPSBzcGxpdCksIGNvbG91ciA9ICJibGFjayIsIGFscGhhID0gMC4wMSkgKwogICMjIGNoYW5nZSB0aGUgYXhpcyBsYWJlbHMKICBsYWJzKHggPSAiQmFyY29kZXMiLCB5ID0gIlVNSSBDb3VudHMiLCBjb2xvdXI9IkNlbGwgRGVzaWduYXRpb24iKSArCiAgIyMgY2hhbmdlIHRoZSBzaXplIG9mIHRoZSBsZWdlbmQKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0xMCkpKSArCiAgIyMgZml4IGF4aXMKICBjb29yZF9maXhlZCgpICsKICAjIyBtYWtlIGl0IGxvb2sgcHJldHR5CiAgdGhlbWVfbGlnaHQoKQoKIyMgcHJpbnQgdGhlIHBsb3QKYmFyY29kZV9wbG90CgojIyBzYXZlIHRoZSBwbG90Cmdnc2F2ZSgiUUNfMTBYX2JhcmNvZGVfcGxvdF81ay5wbmciLCBwbG90ID0gYmFyY29kZV9wbG90LCBkZXZpY2UgPSAicG5nIiwgaGVpZ2h0ID0gMTUsIHdpZHRoID0gMTUsIHVuaXRzID0gImNtIiwgcGF0aCA9ICIuLi9pbWFnZXNfdG9fZXhwb3J0LyIpCmBgYAoKc28gdGhlIG51bWJlciBvZiBjZWxscyB0aGF0IGlzIHJldGFpbmVkIGlzOgpgYGB7ciwgZWNobyA9IEZBTFNFfQp0YWJsZShkZl9iYXJjb2RlcyRjZWxsKQpgYGAKCiMjIyBGaWx0ZXIgdGhlIG9iamVjdAoKYGBge3IgZmlsdGVyIGNlbGxzIHZzIGJhY2tncm91bmR9CiMjIGV4dHJhY3QgdGhlIG5Db3VudCBhbmQgcm93IG5hbWVzIGZyb20gdGhlIFNldXJhdCBvYmplY3QKdXBiIDwtIGRhdGEuZnJhbWUobkNvdW50X1JOQSA9IHRlbng1a0BtZXRhLmRhdGEkbkNvdW50X1JOQSwgcm93Lm5hbWVzID0gcm93bmFtZXModGVueDVrQG1ldGEuZGF0YSkpCgojIyBtYWtlIGJsYW5rIGNvbHVtbiBmb3IgcmFuawp1cGIkcmFuayA8LSBOQQoKIyMgb3JkZXIgYnkgbkNvdW50cwpvcmRlci5zY29yZXMgPC0gb3JkZXIodXBiJG5Db3VudF9STkEsIGRlY3JlYXNpbmcgPSBUUlVFKQoKIyMgYWRkIGEgcmFuayB0byB0aGlzIGNvbHVtbiBiYXNlZCBvbiB0aGUgb3JkZXJlZCBuQ291bnQKdXBiJHJhbmtbb3JkZXIuc2NvcmVzXSA8LSAxOm5yb3codXBiKQoKIyMgaW5zcGVjdAojaGVhZCh1cGIpCgojIyBtYWtlIGEgbGlzdCBvZiBjZWxscyB0byByZXRhaW4gaW4gdGhlIFNldXJhdCBvYmplY3QKa2VlcF9jZWxscyA8LSByb3duYW1lcyh1cGJbd2hpY2godXBiJHJhbmsgPCA3NzYzKSxdKQoKIyMgc3Vic2V0IFNldXJhdCBvYmplY3QgdG8gaW5jbHVkZSBjZWxscyBhbmQgZGlzY2FyZCBiYWNrZ3JvdW5kCnBiX3NleCA8LSBzdWJzZXQodGVueDVrLCBjZWxscyA9IGtlZXBfY2VsbHMpCmBgYAoKIyA0LiBGaWx0ZXIgT3V0IFBvb3ItUXVhbGl0eSBDZWxscyB7LnRhYnNldH0KCiMjIyBGaWx0ZXIgTWl0b2Nob25kcmlhbCAlCgpNaXRvY2hvbmRyaWFsIGNlbGwgY291bnRzCmBgYHtyLCBmaWcuaGVpZ2h0ID0gMTUsIGZpZy53aWR0aCA9IDd9CiMjIGV4dHJhY3QgbWl0b2Nob25kcmlhbCBnZW5lcyAKI21pdG9fZ2VuZXMgPC0gZ3RmW3doaWNoKGd0ZiRWMyA9PSAiclJOQSIpLF0kVjkKI21pdG9fZ2VuZXMgPC0gZ3N1YigiOy4qIiwiIiwgZ3N1YigiZ2VuZV9pZCAiLCAiIiwgbWl0b19nZW5lcykpCiNwYXN0ZSgiVGhlc2UgYXJlIHRoZSBtaXRvY2hvbmRyaWFsIGdlbmVzIikKI2hlYWQobWl0b19nZW5lcykKCiMjIGV4dHJhY3QgbWl0byBnZW5lcwptaXRvX2dlbmVzIDwtIHBiX3NleEBhc3NheXMkUk5BQGNvdW50c0BEaW1uYW1lc1tbMV1dW2dyZXAoIl5QQkFOS0EtTUlUIixwYl9zZXhAYXNzYXlzJFJOQUBjb3VudHNARGltbmFtZXNbWzFdXSldCgojIyBwbG90IHRoZSBnZW5lcyBpbmRpdmlkdWFsbHkKVmxuUGxvdChvYmplY3QgPSBwYl9zZXgsIGZlYXR1cmVzID0gbWl0b19nZW5lcywgcHQuc2l6ZSA9IDAuMDEpCgojIyBtYWtlIGEgcGVyY2VudGFnZSBtaXRvY29uZHJpYWwgZm9yIGVhY2ggY2VsbCAodGhpcyB3aWxsIHdvcmsgYXMgbG9uZyBhcyB5b3UgZmlsdGVyIGNlbGxzIG91dCB3aXRoIHplcm8gY291bnRzKQpwYl9zZXggPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQocGJfc2V4LCBwYXR0ZXJuID0gIl5QQkFOS0EtTUlUIiwgY29sLm5hbWUgPSAicGVyY2VudC5tdCIpCmBgYAoKcGxvdCBwZXJjZW50YWdlIG1pdG9jaG9uZHJpYWwKYGBge3J9CiMjIHBsb3QgZm9yIHBlcmNlbnRhZ2Ugb2YgbWl0b2Nob25kcmlhbCByZWFkcwp2MSA8LSBWbG5QbG90KG9iamVjdCA9IHBiX3NleCwgZmVhdHVyZXMgPSAicGVyY2VudC5tdCIsIHB0LnNpemUgPSAwLjAxKSArCiAgIyMgYWRkIGEgbGluZSB3aGVyZSB3ZSB3aWxsIGZpbHRlcgogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDIwLCBjb2w9ImJsdWUiKSArCiAgIyMgY2hhbmdlIGxhYmVscwogIGxhYnMoeCA9ICIiLHkgPSAiJSBNaXRvY2hvbmRyaWFsIFJlYWRzIiwgdGl0bGUgPSAiTWl0b2Nob25kcmlhbCBwZXIgY2VsbCIpICsKICAjIyByZW1vdmUgbGVnZW5kCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgIyMgY2hhbmdlIGFwcGVhcmFuY2UKICB0aGVtZV9jbGFzc2ljKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0iZ3JleSIpICsKICAjc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMTAwKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpLCB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0yMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0yMCksIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChjb2xvdXI9ImJsYWNrIikpCgojIyBwcmludAp2MQoKIyMgc2F2ZQojZ2dzYXZlKCJ+L2ltYWdlc190b19leHBvcnQvUUNfMTBYX21pdG9fdmlvbGluLnBuZyIsIHBsb3QgPSBRQ19taXRvX3Zpb2xpbiwgZGV2aWNlID0gInBuZyIsIHBhdGggPSBOVUxMLCBzY2FsZSA9IDEsIHdpZHRoID0gMTUsIGhlaWdodCA9IDEwLCB1bml0cyA9ICJjbSIsIGRwaSA9IDMwMCwgbGltaXRzaXplID0gVFJVRSkKYGBgCgpJbiB0aGUgU21hcnQtc2VxMiBkYXRhLCB3ZSB1c2UgYSB0aHJlc2hvbGQgb2YgMjAlLiBObyBjZWxsIGluIG91ciAxMFggZGF0YSBpcyBoaWdoZXIgdGhhbiAxMyUgYW5kIG9ubHkKYGBge3IsIGVjaG8gPSBGQUxTRX0Kc3VtKHBiX3NleEBtZXRhLmRhdGEkcGVyY2VudC5tdCA+IDUpCmBgYApjZWxscyBoYXZlIGEgJSBtaXRvY2hvbmRyaWFsIHJlYWRzIGFib3ZlIDUlLiAKCiMjIyBuR2VuZXMgZmlsdGVyCgpwbG90IGluZGl2aWR1YWwgdmlvbGluIHBsb3RzCmBgYHtyfQojIyBuR2VuZXMgcGxvdApnZW5lX3Bsb3RfNWsgPC0gVmxuUGxvdChvYmplY3QgPSBwYl9zZXgsIGZlYXR1cmVzID0gIm5GZWF0dXJlX1JOQSIsIHB0LnNpemUgPSAwLjAxKQoKIyMgaW1wcm92ZSB0aGUgYWVzdGhldGljcwpnZW5lX3Bsb3RfNWsgPC0gZ2VuZV9wbG90XzVrICsgCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMjAwLCBjb2w9ImJsdWUiKSArCiAgbGFicyh4ID0gIiIseSA9ICJuR2VuZSIsIHRpdGxlID0gIkdlbmVzIHBlciBjZWxsIikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSJncmV5IikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDMwMDApKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCksIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTIwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTIwKSwgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KGNvbG91cj0iYmxhY2siKSkKCiMjIG5VTUkgcGxvdApudW1pX3Bsb3RfNWsgPC0gVmxuUGxvdChvYmplY3QgPSBwYl9zZXgsIGZlYXR1cmVzID0gIm5Db3VudF9STkEiLCBwdC5zaXplID0gMC4wMSkKCiMjIGltcHJvdmUgYWVzdGhldGljcwpudW1pX3Bsb3RfNWsgPC0gbnVtaV9wbG90XzVrICsKICBsYWJzKHggPSAiIix5ID0gIm5VTUkiLCB0aXRsZSA9ICJVTUlzIHBlciBjZWxsIikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSJncmV5IikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDMwMDApKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCksIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTIwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTIwKSwgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KGNvbG91cj0iYmxhY2siKSkKCiMjIHBsb3QgdG9nZXRoZXIKZ3JpZC5hcnJhbmdlKGdlbmVfcGxvdF81aywgbnVtaV9wbG90XzVrLCBuY29sID0gMiwgdG9wPXRleHRHcm9iKCI1SyBjZWxscyAxMFgiLCBncD1ncGFyKGZvbnRzaXplPTE1LGZvbnQ9OCkpKQoKIyMgc2F2ZSBuR2VuZSBwbG90IG9uIGl0cyBvd24KI2dnc2F2ZSgiUUNfbmdlbmVfcGxvdC5wZGYiLCBwbG90ID0gZ2VuZTEsIGRldmljZSA9ICJwZGYiLCBoZWlnaHQgPSA1LCB3aWR0aCA9IDUsIHVuaXRzID0gImluIiwgcGF0aCA9ICIvVXNlcnMvYXIxOS9EZXNrdG9wL1BoRC9HQ1NLT19BbmFseXNpcyIpCmBgYAoKcGxvdCB0d28gbWV0cmljcyB0b2dldGhlcgpgYGB7ciwgZmlnLmhlaWdodCA9IDgsIGZpZy53aWR0aCA9IDh9CiMjIG1ha2UgYSBkYXRhZnJhbWUgZm9yIGltcG9ydGFudCBmaWx0ZXJpbmcgbWV0cmljcwpkZiA8LSBkYXRhLmZyYW1lKG5Db3VudCA9IGxvZzEwKHBiX3NleEBtZXRhLmRhdGEkbkNvdW50X1JOQSksIG5HZW5lcyA9IHBiX3NleEBtZXRhLmRhdGEkbkZlYXR1cmVfUk5BLCBwZXJjZW50X210ID0gcGJfc2V4QG1ldGEuZGF0YSRwZXJjZW50Lm10LCBleHBlcmltZW50ID0gcGJfc2V4QG1ldGEuZGF0YSRleHBlcmltZW50KQoKIyMgcGxvdCBtYWluIGRvdHBsb3QKcGxvdDEgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IG5Db3VudCwgeSA9IG5HZW5lcykpICsKICAjZ2VvbV9wb2ludChhZXMoKSwgc2l6ZSA9IDAuMSkgKwogIGdlb21faGV4KGJpbnMgPSAyMDApICsKICAjZ2VvbV9ydWcoKSArIAogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIk51bWJlciBvZiBEZXRlY3RlZCBHZW5lcyIpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAibG9nMTAoTnVtYmVyIG9mIFRvdGFsIFVNSSkiKSArIAogIHRoZW1lX3B1YnIoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MjAwKQoKIyMgcGxvdCBkZW5zaXR5IHBsb3QgeApkZW5zIDwtIGRlbnNpdHkoZGYkbkNvdW50KQpkdCA8LSBkYXRhLmZyYW1lKHg9ZGVucyR4LCB5PWRlbnMkeSkKcHJvYnMgPC0gYygwLCAwLjI1LCAwLjUsIDAuNzUsIDEpCnF1YW50aWxlcyA8LSBxdWFudGlsZShkZiRuQ291bnQsIHByb2I9cHJvYnMpCmR0JHF1YW50IDwtIGZhY3RvcihmaW5kSW50ZXJ2YWwoZHQkeCxxdWFudGlsZXMpKQpkZW5zMSA8LSBnZ3Bsb3QoZHQsIGFlcyh4LHkpKSArIGdlb21fbGluZSgpICsgZ2VvbV9yaWJib24oYWVzKHltaW49MCwgeW1heD15LCBmaWxsPXF1YW50KSkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXF1YW50aWxlcykgKyBzY2FsZV9maWxsX2JyZXdlcihndWlkZT0ibm9uZSIpICsgdGhlbWVfdm9pZCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQojIyBwbG90IGRlbnNpdHkgcGxvdCB5CiMjIG9sZCBtZXRob2QKIyBkZW5zMiA8LSBnZ3Bsb3QoZGYsIGFlcyh4ID0gbkdlbmVzLCB5ID0gZXhwZXJpbWVudCkpICsKIyAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMikgKwojICAgdGhlbWVfdm9pZCgpICsKIyAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwojICAgY29vcmRfZmxpcCgpCiMjIG5ldyBtZXRob2QKZGVucyA8LSBkZW5zaXR5KGRmJG5HZW5lcykKZHQgPC0gZGF0YS5mcmFtZSh4PWRlbnMkeCwgeT1kZW5zJHkpCnByb2JzIDwtIGMoMCwgMC4yNSwgMC41LCAwLjc1LCAxKQpxdWFudGlsZXMgPC0gcXVhbnRpbGUoZGYkbkdlbmVzLCBwcm9iPXByb2JzKQpkdCRxdWFudCA8LSBmYWN0b3IoZmluZEludGVydmFsKGR0JHgscXVhbnRpbGVzKSkKZGVuczIgPC0gZ2dwbG90KGR0LCBhZXMoeCx5KSkgKyBnZW9tX2xpbmUoKSArIGdlb21fcmliYm9uKGFlcyh5bWluPTAsIHltYXg9eSwgZmlsbD1xdWFudCkpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1xdWFudGlsZXMpICsgc2NhbGVfZmlsbF9icmV3ZXIoZ3VpZGU9Im5vbmUiKSArIHRoZW1lX3ZvaWQoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGNvb3JkX2ZsaXAoKQoKIyMgcGxvdCB0b2dldGhlcgpRQ19jb21wb3NpdGVfcGxvdCA8LSBkZW5zMSArIHBsb3Rfc3BhY2VyKCkgKyBwbG90MSArIGRlbnMyICsgcGxvdF9sYXlvdXQobmNvbCA9IDIsIG5yb3cgPSAyLCB3aWR0aHMgPSBjKDQsIDEpLCBoZWlnaHRzID0gYygxLCA0KSkKCiMjIHByaW50ClFDX2NvbXBvc2l0ZV9wbG90CgojIyBzYXZlCmdnc2F2ZSgiLi4vaW1hZ2VzX3RvX2V4cG9ydC9RQ18xMFhfY29tcG9zaXRlX3Bsb3QucG5nIiwgcGxvdCA9IFFDX2NvbXBvc2l0ZV9wbG90LCBkZXZpY2UgPSAicG5nIiwgcGF0aCA9IE5VTEwsIHNjYWxlID0gMSwgd2lkdGggPSAyMCwgaGVpZ2h0ID0gMjAsIHVuaXRzID0gImNtIiwgZHBpID0gMzAwLCBsaW1pdHNpemUgPSBUUlVFKQpgYGAKCiMjIyBGaWx0ZXJpbmcKClRoZSB0aHJlc2hvbGQgdXNlZCBpbiB0aGUgbWFsYXJpYSBjZWxsIGF0bGFzIHdhcyAyMzAgZm9yIFBiIGJ1dCB0aGlzIGlzIGRlcGVuZGVudCBvbiBzZXF1ZW5jaW5nIGRlcHRoIGV0Yy4KV2UgY2FuIHBsb3QgdGhlIG51bWJlciBvZiBjZWxscyByZWNvdmVyZWQgZm9yIGEgcmFuZ2Ugb2YgdGhyZXNob2xkczoKYGBge3J9CnBhc3RlKCJvcmlnaW5hbCBudW1iZXIgb2YgY2VsbHMgPSIsIG5yb3cocGJfc2V4QG1ldGEuZGF0YSkpCnBhc3RlKCJ3aXRoID4xNTAgZmlsdGVyID0iLCBucm93KHBiX3NleEBtZXRhLmRhdGFbcGJfc2V4QG1ldGEuZGF0YSRuRmVhdHVyZV9STkEgPiAxNTAsIF0pKQpwYXN0ZSgid2l0aCA+MjAwIGZpbHRlciA9IiwgbnJvdyhwYl9zZXhAbWV0YS5kYXRhW3BiX3NleEBtZXRhLmRhdGEkbkZlYXR1cmVfUk5BID4gMjAwLCBdKSkKcGFzdGUoIndpdGggPjIzMCBmaWx0ZXIgPSIsIG5yb3cocGJfc2V4QG1ldGEuZGF0YVtwYl9zZXhAbWV0YS5kYXRhJG5GZWF0dXJlX1JOQSA+IDIzMCwgXSkpCmBgYAoKU2luY2Ugd2UgaGF2ZSBhbHJlYWR5IGZpbHRlcmVkIG9uIG5VTUksIHdlIHdpbGwgZmlsdGVyIHdpdGggMjAwLgoKYGBge3J9CiMjIG51bWJlciBvZiBjZWxscyBiZWZvcmUgZmlsdGVyaW5nCnBiX3NleF9wcmVfZmlsdGVyX25DZWxscyA8LSBucm93KHBiX3NleEBtZXRhLmRhdGEpCiMjIGZpbHRlciBvYmplY3QKcGJfc2V4IDwtIHN1YnNldChwYl9zZXgsIHN1YnNldCA9IG5GZWF0dXJlX1JOQSA+IDIwMCkKIyMgbnVtYmVyIG9mIGNlbGxzIGFmdGVyIGZpbHRlcmluZwpwYl9zZXhfcG9zdF9maWx0ZXJfbkNlbGxzIDwtIG5yb3cocGJfc2V4QG1ldGEuZGF0YSkKIyMgcHJpbnQgcmVzdWx0cyBvZiBmaWx0ZXJpbmcKcGFzdGUoIm51bWJlciBvZiBjZWxscyBwcmUtZmlsdGVyIiwgcGJfc2V4X3ByZV9maWx0ZXJfbkNlbGxzKQpwYXN0ZSgibnVtYmVyIG9mIGNlbGxzIHBvc3QtZmlsdGVyIiwgcGJfc2V4X3Bvc3RfZmlsdGVyX25DZWxscykKcGFzdGUoKHBiX3NleF9wcmVfZmlsdGVyX25DZWxscyAtIHBiX3NleF9wb3N0X2ZpbHRlcl9uQ2VsbHMpLCAiY2VsbHMgd2VyZSByZW1vdmVkIGJ5IGZpbHRlcmluZyBvbiBudW1iZXIgb2YgZ2VuZXMuIikKYGBgCgojIDUuIERpbWVuc2lvbmFsaXR5IFJlZHVjdGlvbiBhbmQgQ2x1c3RlcmluZyB7LnRhYnNldH0KCiMjIyBQcmVwYXJlIGRhdGEKYGBge3J9CiMjIG5vcm1hbGlzZSBvYmplY3QKcGJfc2V4IDwtIE5vcm1hbGl6ZURhdGEocGJfc2V4LCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCBzY2FsZS5mYWN0b3IgPSAxMDAwMCkKCiMjIGZpbmQgdmFyaWFibGUgZ2VuZXMKcGJfc2V4IDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKHBiX3NleCwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCBuZmVhdHVyZXMgPSAyMDAwKQoKIyMgc2NhbGUgdGhlIGRhdGEKYWxsLmdlbmVzIDwtIHJvd25hbWVzKHBiX3NleCkKcGJfc2V4IDwtIFNjYWxlRGF0YShwYl9zZXgsIGZlYXR1cmVzID0gYWxsLmdlbmVzKQpgYGAKCiMjIyBQQ0EKYGBge3J9CiMjIHJ1biBQQ0EKcGJfc2V4IDwtIFJ1blBDQShwYl9zZXgsIGZlYXR1cmVzID0gVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSBwYl9zZXgpKQoKIyMgcGxvdCAKRGltUGxvdChwYl9zZXgsIHJlZHVjdGlvbiA9ICJwY2EiKQoKIyMgZWxib3cgcGxvdApFbGJvd1Bsb3QocGJfc2V4LCBuZGltcyA9IDMwLCByZWR1Y3Rpb24gPSAicGNhIikKYGBgCgojIyMgVU1BUApgYGB7cn0KIyMgcnVuIFVNQVAKcGJfc2V4IDwtIFJ1blVNQVAocGJfc2V4LCBkaW1zID0gMToxMCwgc2VlZC51c2UgPSAxMjM0LCBuLm5laWdoYm9ycyA9IDE1MCkKCiMjIHBsb3QKRGltUGxvdChwYl9zZXgsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiaWRlbnQiLCBsYWJlbCA9IFRSVUUpCgojIyBUaGVzZSBhcmUgdGhlIHBhcmFtZXRlcnMgdXNlZCBpbiB0aGUgbWVyZ2UgVU1BUAojcGJfc2V4IDwtIFJ1blVNQVAocGJfc2V4LCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MTAsIG4ubmVpZ2hib3JzID0gMTUwLCBzZWVkLnVzZSA9IDEyMzQsIG1pbi5kaXN0ID0gMC40LCByZXB1bHNpb24uc3RyZW5ndGggPSAwLjAzLCBsb2NhbC5jb25uZWN0aXZpdHkgPSAxNTApCiNEaW1QbG90KHBiX3NleCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJpZGVudCIsIGxhYmVsID0gVFJVRSkKYGBgCgpjb2xvdXIgd2l0aCBhIGZldyBtYXJrZXIgZ2VuZXM6CmBgYHtyLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gOH0KIyBQQkFOS0EtMDUxNTAwMCAtIHAyNSAtIGZlbWFsZQojIFBCQU5LQS0xMjEyNjAwIC0gSEFQMiAtIG1hbGUKIyBQQkFOS0EtMDYwMDYwMCAtIE5FSzMgLSBtYWxlCiMgUEJBTktBLTA4MzEwMDAgLSBNU1AxIC0gbGF0ZSBhc2V4dWFsCiMgUEJBTktBLTEzMTU3MDAgLSBST04yIC0gKGFzZXh1YWxzIGFuZCBzb21lIG1hbGU/KQojIFBCQU5LQS0wNDE2MTAwIC0gZHluZW5pbiBoZWF2eSBjaGFpbiAtIG1hbGUgLSB1c2VkIGluIDgyMCBsaW5lCiMgUEJBTktBLTEzMTk1MDAgLSBDQ1AyIC0gZmVtYWxlIC0gdXNlZCBpbiA4MjAgbGluZSAKIyBQQkFOS0EtMTQzNzUwMCAtIEFQMi1HIC0gc2V1eGFsIGNvbW1pdG1lbnQgZ2VuZQojIFBCQU5LQS0xMTAyMjAwIC0gTVNQOCAtIGVhcmx5IGFzZXh1YWwgKGZyb20gQm96ZGVjaCBwYXBlcikKCkZlYXR1cmVQbG90KHBiX3NleCwgZmVhdHVyZXMgPSBjKCJQQkFOS0EtMDUxNTAwMCIsICJQQkFOS0EtMTIxMjYwMCIsIlBCQU5LQS0wNjAwNjAwIiwgIlBCQU5LQS0wODMxMDAwIiwgIlBCQU5LQS0xMzE1NzAwIiwgIlBCQU5LQS0wNDE2MTAwIiwgIlBCQU5LQS0xMzE5NTAwIiwgIlBCQU5LQS0xNDM3NTAwIiwgIlBCQU5LQS0xMTAyMjAwIikpCmBgYAoKIyMjIENsdXN0ZXJpbmcKYGBge3J9CnBiX3NleCA8LSBGaW5kTmVpZ2hib3JzKHBiX3NleCwgZGltcyA9IDE6MjEpCnBiX3NleCA8LSBGaW5kQ2x1c3RlcnMocGJfc2V4LCByZXNvbHV0aW9uID0gMSkKYGBgCgojIDYuIFJlbW92ZSBEb3VibGV0cyB7LnRhYnNldH0KCiMjIyBEb3VibGV0RmluZGVyCgpEb3VibGV0RmluZGVyIGZ1bmN0aW9uCmBgYHtyfQojIyBEb3VibGV0RmluZGVyIHNob3VsZCBiZSBhYmxlIHRvIGJlIGluc3RhbGxlZCBhbmQgcnVuIGFzIHNvOgojZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCdjaHJpcy1tY2dpbm5pcy11Y3NmL0RvdWJsZXRGaW5kZXInKQojbGlicmFyeShkb3VibGV0RmluZGVyKSAjYWxsb3dzIHJlbW92YWwgb2YgZG91YmxldHMKIyMgYnV0IHRoZXJlIHNlZW1zIHRvIGJlIHNvbWUgcHJvYmxlbXMgd2l0aCB0aGlzIHNvIHdlIHdpbGwgcnVuIGl0IGZyb20gdGhlIGdpdGh1YiBjb2RlCgojIyB0aGUgZG91YmxldCBmaW5kZXIgZnVuY3Rpb24KZG91YmxldEZpbmRlcl92MyA8LSBmdW5jdGlvbihzZXUsIFBDcywgcE4gPSAwLjI1LCBwSywgbkV4cCwgcmV1c2UucEFOTiA9IEZBTFNFLCBzY3QgPSBGQUxTRSkgewogIHJlcXVpcmUoU2V1cmF0KTsgcmVxdWlyZShmaWVsZHMpOyByZXF1aXJlKEtlcm5TbW9vdGgpCgogICMjIEdlbmVyYXRlIG5ldyBsaXN0IG9mIGRvdWJsZXQgY2xhc3NpZmljYXRvbnMgZnJvbSBleGlzdGluZyBwQU5OIHZlY3RvciB0byBzYXZlIHRpbWUKICBpZiAocmV1c2UucEFOTiAhPSBGQUxTRSApIHsKICAgIHBBTk4ub2xkIDwtIHNldUBtZXRhLmRhdGFbICwgcmV1c2UucEFOTl0KICAgIGNsYXNzaWZpY2F0aW9ucyA8LSByZXAoIlNpbmdsZXQiLCBsZW5ndGgocEFOTi5vbGQpKQogICAgY2xhc3NpZmljYXRpb25zW29yZGVyKHBBTk4ub2xkLCBkZWNyZWFzaW5nPVRSVUUpWzE6bkV4cF1dIDwtICJEb3VibGV0IgogICAgc2V1QG1ldGEuZGF0YVssIHBhc3RlKCJERi5jbGFzc2lmaWNhdGlvbnMiLHBOLHBLLG5FeHAsc2VwPSJfIildIDwtIGNsYXNzaWZpY2F0aW9ucwogICAgcmV0dXJuKHNldSkKICB9CgogIGlmIChyZXVzZS5wQU5OID09IEZBTFNFKSB7CiAgICAjIyBNYWtlIG1lcmdlZCByZWFsLWFydGlmaWNhbCBkYXRhCiAgICByZWFsLmNlbGxzIDwtIHJvd25hbWVzKHNldUBtZXRhLmRhdGEpCiAgICBkYXRhIDwtIHNldUBhc3NheXMkUk5BQGNvdW50c1ssIHJlYWwuY2VsbHNdCiAgICBuX3JlYWwuY2VsbHMgPC0gbGVuZ3RoKHJlYWwuY2VsbHMpCiAgICBuX2RvdWJsZXRzIDwtIHJvdW5kKG5fcmVhbC5jZWxscy8oMSAtIHBOKSAtIG5fcmVhbC5jZWxscykKICAgIHByaW50KHBhc3RlKCJDcmVhdGluZyIsbl9kb3VibGV0cywiYXJ0aWZpY2lhbCBkb3VibGV0cy4uLiIsc2VwPSIgIikpCiAgICByZWFsLmNlbGxzMSA8LSBzYW1wbGUocmVhbC5jZWxscywgbl9kb3VibGV0cywgcmVwbGFjZSA9IFRSVUUpCiAgICByZWFsLmNlbGxzMiA8LSBzYW1wbGUocmVhbC5jZWxscywgbl9kb3VibGV0cywgcmVwbGFjZSA9IFRSVUUpCiAgICBkb3VibGV0cyA8LSAoZGF0YVssIHJlYWwuY2VsbHMxXSArIGRhdGFbLCByZWFsLmNlbGxzMl0pLzIKICAgIGNvbG5hbWVzKGRvdWJsZXRzKSA8LSBwYXN0ZSgiWCIsIDE6bl9kb3VibGV0cywgc2VwID0gIiIpCiAgICBkYXRhX3dkb3VibGV0cyA8LSBjYmluZChkYXRhLCBkb3VibGV0cykKCiAgICAjIyBTdG9yZSBpbXBvcnRhbnQgcHJlLXByb2Nlc3NpbmcgaW5mb3JtYXRpb24KICAgIG9yaWcuY29tbWFuZHMgPC0gc2V1QGNvbW1hbmRzCgogICAgIyMgUHJlLXByb2Nlc3MgU2V1cmF0IG9iamVjdAogICAgaWYgKHNjdCA9PSBGQUxTRSkgewogICAgICBwcmludCgiQ3JlYXRpbmcgU2V1cmF0IG9iamVjdC4uLiIpCiAgICAgIHNldV93ZG91YmxldHMgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IGRhdGFfd2RvdWJsZXRzKQoKICAgICAgcHJpbnQoIk5vcm1hbGl6aW5nIFNldXJhdCBvYmplY3QuLi4iKQogICAgICBzZXVfd2RvdWJsZXRzIDwtIE5vcm1hbGl6ZURhdGEoc2V1X3dkb3VibGV0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gb3JpZy5jb21tYW5kcyROb3JtYWxpemVEYXRhLlJOQUBwYXJhbXMkbm9ybWFsaXphdGlvbi5tZXRob2QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZS5mYWN0b3IgPSBvcmlnLmNvbW1hbmRzJE5vcm1hbGl6ZURhdGEuUk5BQHBhcmFtcyRzY2FsZS5mYWN0b3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBvcmlnLmNvbW1hbmRzJE5vcm1hbGl6ZURhdGEuUk5BQHBhcmFtcyRtYXJnaW4pCgogICAgICBwcmludCgiRmluZGluZyB2YXJpYWJsZSBnZW5lcy4uLiIpCiAgICAgIHNldV93ZG91YmxldHMgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoc2V1X3dkb3VibGV0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Rpb24ubWV0aG9kID0gb3JpZy5jb21tYW5kcyRGaW5kVmFyaWFibGVGZWF0dXJlcy5STkEkc2VsZWN0aW9uLm1ldGhvZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2Vzcy5zcGFuID0gb3JpZy5jb21tYW5kcyRGaW5kVmFyaWFibGVGZWF0dXJlcy5STkEkbG9lc3Muc3BhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGlwLm1heCA9IG9yaWcuY29tbWFuZHMkRmluZFZhcmlhYmxlRmVhdHVyZXMuUk5BJGNsaXAubWF4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4uZnVuY3Rpb24gPSBvcmlnLmNvbW1hbmRzJEZpbmRWYXJpYWJsZUZlYXR1cmVzLlJOQSRtZWFuLmZ1bmN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BlcnNpb24uZnVuY3Rpb24gPSBvcmlnLmNvbW1hbmRzJEZpbmRWYXJpYWJsZUZlYXR1cmVzLlJOQSRkaXNwZXJzaW9uLmZ1bmN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bS5iaW4gPSBvcmlnLmNvbW1hbmRzJEZpbmRWYXJpYWJsZUZlYXR1cmVzLlJOQSRudW0uYmluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJpbm5pbmcubWV0aG9kID0gb3JpZy5jb21tYW5kcyRGaW5kVmFyaWFibGVGZWF0dXJlcy5STkEkYmlubmluZy5tZXRob2QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmZlYXR1cmVzID0gb3JpZy5jb21tYW5kcyRGaW5kVmFyaWFibGVGZWF0dXJlcy5STkEkbmZlYXR1cmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4uY3V0b2ZmID0gb3JpZy5jb21tYW5kcyRGaW5kVmFyaWFibGVGZWF0dXJlcy5STkEkbWVhbi5jdXRvZmYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzcGVyc2lvbi5jdXRvZmYgPSBvcmlnLmNvbW1hbmRzJEZpbmRWYXJpYWJsZUZlYXR1cmVzLlJOQSRkaXNwZXJzaW9uLmN1dG9mZikKCiAgICAgIHByaW50KCJTY2FsaW5nIGRhdGEuLi4iKQogICAgICBzZXVfd2RvdWJsZXRzIDwtIFNjYWxlRGF0YShzZXVfd2RvdWJsZXRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IG9yaWcuY29tbWFuZHMkU2NhbGVEYXRhLlJOQSRmZWF0dXJlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwudXNlID0gb3JpZy5jb21tYW5kcyRTY2FsZURhdGEuUk5BJG1vZGVsLnVzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG8uc2NhbGUgPSBvcmlnLmNvbW1hbmRzJFNjYWxlRGF0YS5STkEkZG8uc2NhbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvLmNlbnRlciA9IG9yaWcuY29tbWFuZHMkU2NhbGVEYXRhLlJOQSRkby5jZW50ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlLm1heCA9IG9yaWcuY29tbWFuZHMkU2NhbGVEYXRhLlJOQSRzY2FsZS5tYXgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsb2NrLnNpemUgPSBvcmlnLmNvbW1hbmRzJFNjYWxlRGF0YS5STkEkYmxvY2suc2l6ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLmNlbGxzLnRvLmJsb2NrID0gb3JpZy5jb21tYW5kcyRTY2FsZURhdGEuUk5BJG1pbi5jZWxscy50by5ibG9jaykKCiAgICAgIHByaW50KCJSdW5uaW5nIFBDQS4uLiIpCiAgICAgIHNldV93ZG91YmxldHMgPC0gUnVuUENBKHNldV93ZG91YmxldHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gb3JpZy5jb21tYW5kcyRTY2FsZURhdGEuUk5BJGZlYXR1cmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucGNzID0gbGVuZ3RoKFBDcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldi5wY2EgPSAgb3JpZy5jb21tYW5kcyRSdW5QQ0EuUk5BJHJldi5wY2EsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdlaWdodC5ieS52YXIgPSBvcmlnLmNvbW1hbmRzJFJ1blBDQS5STkEkd2VpZ2h0LmJ5LnZhciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1GQUxTRSkKICAgICAgcGNhLmNvb3JkIDwtIHNldV93ZG91YmxldHNAcmVkdWN0aW9ucyRwY2FAY2VsbC5lbWJlZGRpbmdzWyAsIFBDc10KICAgICAgY2VsbC5uYW1lcyA8LSByb3duYW1lcyhzZXVfd2RvdWJsZXRzQG1ldGEuZGF0YSkKICAgICAgbkNlbGxzIDwtIGxlbmd0aChjZWxsLm5hbWVzKQogICAgICBybShzZXVfd2RvdWJsZXRzKTsgZ2MoKSAjIEZyZWUgdXAgbWVtb3J5CiAgICB9CgogICAgaWYgKHNjdCA9PSBUUlVFKSB7CiAgICAgIHJlcXVpcmUoc2N0cmFuc2Zvcm0pCiAgICAgIHByaW50KCJDcmVhdGluZyBTZXVyYXQgb2JqZWN0Li4uIikKICAgICAgc2V1X3dkb3VibGV0cyA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gZGF0YV93ZG91YmxldHMpCgogICAgICBwcmludCgiUnVubmluZyBTQ1RyYW5zZm9ybS4uLiIpCiAgICAgIHNldV93ZG91YmxldHMgPC0gU0NUcmFuc2Zvcm0oc2V1X3dkb3VibGV0cykKCiAgICAgIHByaW50KCJSdW5uaW5nIFBDQS4uLiIpCiAgICAgIHNldV93ZG91YmxldHMgPC0gUnVuUENBKHNldV93ZG91YmxldHMsIG5wY3MgPSBsZW5ndGgoUENzKSkKICAgICAgcGNhLmNvb3JkIDwtIHNldV93ZG91YmxldHNAcmVkdWN0aW9ucyRwY2FAY2VsbC5lbWJlZGRpbmdzWyAsIFBDc10KICAgICAgY2VsbC5uYW1lcyA8LSByb3duYW1lcyhzZXVfd2RvdWJsZXRzQG1ldGEuZGF0YSkKICAgICAgbkNlbGxzIDwtIGxlbmd0aChjZWxsLm5hbWVzKQogICAgICBybShzZXVfd2RvdWJsZXRzKTsgZ2MoKQogICAgfQoKICAgICMjIENvbXB1dGUgUEMgZGlzdGFuY2UgbWF0cml4CiAgICBwcmludCgiQ2FsY3VsYXRpbmcgUEMgZGlzdGFuY2UgbWF0cml4Li4uIikKICAgIGRpc3QubWF0IDwtIGZpZWxkczo6cmRpc3QocGNhLmNvb3JkKQoKICAgICMjIENvbXB1dGUgcEFOTgogICAgcHJpbnQoIkNvbXB1dGluZyBwQU5OLi4uIikKICAgIHBBTk4gPC0gYXMuZGF0YS5mcmFtZShtYXRyaXgoMEwsIG5yb3cgPSBuX3JlYWwuY2VsbHMsIG5jb2wgPSAxKSkKICAgIHJvd25hbWVzKHBBTk4pIDwtIHJlYWwuY2VsbHMKICAgIGNvbG5hbWVzKHBBTk4pIDwtICJwQU5OIgogICAgayA8LSByb3VuZChuQ2VsbHMgKiBwSykKICAgIGZvciAoaSBpbiAxOm5fcmVhbC5jZWxscykgewogICAgICBuZWlnaGJvcnMgPC0gb3JkZXIoZGlzdC5tYXRbLCBpXSkKICAgICAgbmVpZ2hib3JzIDwtIG5laWdoYm9yc1syOihrICsgMSldCiAgICAgIG5laWdoYm9yLm5hbWVzIDwtIHJvd25hbWVzKGRpc3QubWF0KVtuZWlnaGJvcnNdCiAgICAgIHBBTk4kcEFOTltpXSA8LSBsZW5ndGgod2hpY2gobmVpZ2hib3JzID4gbl9yZWFsLmNlbGxzKSkvawogICAgfQoKICAgIHByaW50KCJDbGFzc2lmeWluZyBkb3VibGV0cy4uIikKICAgIGNsYXNzaWZpY2F0aW9ucyA8LSByZXAoIlNpbmdsZXQiLG5fcmVhbC5jZWxscykKICAgIGNsYXNzaWZpY2F0aW9uc1tvcmRlcihwQU5OJHBBTk5bMTpuX3JlYWwuY2VsbHNdLCBkZWNyZWFzaW5nPVRSVUUpWzE6bkV4cF1dIDwtICJEb3VibGV0IgogICAgc2V1QG1ldGEuZGF0YVssIHBhc3RlKCJwQU5OIixwTixwSyxuRXhwLHNlcD0iXyIpXSA8LSBwQU5OW3Jvd25hbWVzKHNldUBtZXRhLmRhdGEpLCAxXQogICAgc2V1QG1ldGEuZGF0YVssIHBhc3RlKCJERi5jbGFzc2lmaWNhdGlvbnMiLHBOLHBLLG5FeHAsc2VwPSJfIildIDwtIGNsYXNzaWZpY2F0aW9ucwogICAgcmV0dXJuKHNldSkKICB9Cn0KIyMgdXNhZ2U6IGh0dHBzOi8vcmRyci5pby9naXRodWIvY2hyaXMtbWNnaW5uaXMtdWNzZi9Eb3VibGV0RmluZGVyL21hbi9kb3VibGV0RmluZGVyX3YzLmh0bWwKIyMgc291cmNlOiBodHRwczovL2dpdGh1Yi5jb20vY2hyaXMtbWNnaW5uaXMtdWNzZi9Eb3VibGV0RmluZGVyL2Jsb2IvbWFzdGVyL1IvZG91YmxldEZpbmRlcl92My5SCmBgYAoKUnVuIERvdWJsZXRGaW5kZXIKYGBge3J9CiMgdGhlIHR1dG9yaWFsIHJlY29tbWVuZHMgdXNpbmcgdGhpcyBhcyBhbiBhcHByb3hpbWF0aW9uOgojbkV4cF9wb2kgPC0gcm91bmQoMC4xNSpucm93KHBiX3NleEBtZXRhLmRhdGEpKQojYnV0IGEgbW9yZSBhcHByb3ByaWF0ZSBhcHByb3hpbWF0aW9uIGlzIHRoYXQgdGhlIGV4cGVjdGVkIG51bWJlciBvZiBkb3VibGV0cyBpcyB+MSUgcGVyIDEwMDAgY2VsbHMgc286Cm5FeHBfcG9pIDwtIHJvdW5kKCgwLjAxKihucm93KHBiX3NleEBtZXRhLmRhdGEpLzEwMDApKSpucm93KHBiX3NleEBtZXRhLmRhdGEpKQojcnVuIGRvdWJsZXQgZmluZGVyOgpwYl9zZXggPC0gZG91YmxldEZpbmRlcl92MyhwYl9zZXgsIFBDcyA9IDE6MjEsIHBOID0gMC4yNSwgcEsgPSAwLjAxLCBuRXhwID0gbkV4cF9wb2ksIHJldXNlLnBBTk4gPSBGQUxTRSwgc2N0ID0gRkFMU0UpCmBgYAoKcmVzdWx0cyBpbjoKYGBge3J9CnRhYmxlKHBiX3NleEBtZXRhLmRhdGEkREYuY2xhc3NpZmljYXRpb25zXzAuMjVfMC4wMV80NDApCmBgYAoKIyMjIFZhbGlkYXRpb24gYW5kIHZpc3VhbGlzYXRpb24gb2YgZG91YmxldHMKCnZpc3VhbGlzZSB3aGVyZSBkb3VibGV0cyBhcmU6CmBgYHtyfQpkb3VibGV0LmNlbGxzIDwtIGMocm93bmFtZXMocGJfc2V4QG1ldGEuZGF0YVtwYl9zZXhAbWV0YS5kYXRhJERGLmNsYXNzaWZpY2F0aW9uc18wLjI1XzAuMDFfNDQwID09ICJEb3VibGV0IixdKSkKZDEgPC0gRGltUGxvdChwYl9zZXgsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgY2VsbHMuaGlnaGxpZ2h0ID0gZG91YmxldC5jZWxscywgc2l6ZXMuaGlnaGxpZ2h0ID0gMikKI1RTTkVQbG90KG9iamVjdCA9IHBiX3NleCwgY2VsbHMuaGlnaGxpZ2h0ID0gZG91YmxldC5jZWxscywgZG8ucmV0dXJuID0gVFJVRSwgKQpkb3VibGV0MSA8LSBkMSArIGNvb3JkX2ZpeGVkKCkgKyB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpCgojIyBwbG90IGNsdXN0ZXJzCmNsdXN0ZXJfcGxvdCA8LSBEaW1QbG90KHBiX3NleCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJpZGVudCIsIGxhYmVsID0gVFJVRSkKCmRvdWJsZXQxICsgY2x1c3Rlcl9wbG90CmBgYAoKYGBge3J9ClZsblBsb3Qob2JqZWN0ID0gcGJfc2V4LCBmZWF0dXJlcyA9ICJwQU5OXzAuMjVfMC4wMV80NDAiLCBwdC5zaXplID0gMC4xKQpgYGAKCmBgYHtyfQojIyBleHRyYWN0IG1ldGEgZGF0YSBjb2xzIG9mIGludGVyZXN0CmRmIDwtIHBiX3NleEBtZXRhLmRhdGFbLGMoIlJOQV9zbm5fcmVzLjEiLCJERi5jbGFzc2lmaWNhdGlvbnNfMC4yNV8wLjAxXzQ0MCIpXQoKIyMgbWFrZSBhIHRhYmxlIGZyb20gZG91YmxldHMKZGYgPC0gZGF0YS5mcmFtZShyYmluZCh0YWJsZShkZikpKQoKIyMgYW1tZW5kIHJvd25hbWVzCmRmJGNsdXN0ZXIgPC0gcm93bmFtZXMoZGYpCgojIyBjYWxjdWxhdGUgcGVyY2VudGFnZXMgb2YgY2VsbHMgdGhhdCBhcmUgZG91YmxldHMKZGYkcGNfZG91YmxldCA8LSAoKGRmWywxXSkvKChkZlssMV0pICsgZGZbLDJdKSkqMTAwCgojIyBpbnNwZWN0CiNrYWJsZShkZltvcmRlcihkZiRwY19kb3VibGV0KSxdKQoKIyMgcGxvdCAKZ2dwbG90KGRhdGE9ZGYsIGFlcyh4PWNsdXN0ZXIsIHk9cGNfZG91YmxldCkpICsKICBnZW9tX2NvbChmaWxsPSJzdGVlbGJsdWUiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKIyMjIEZpbHRlciBkb3VibGV0cwoKSXQgZGVmaW5pdGVseSBzZWVtcyBsaWtlIHRoZXJlIGFyZSBzb21lIGJpYXNlcyBpbiBkb3VibGV0IGRldGVjdGlvbi4gRmV3ZXIgZG91YmxldHMgaW4gdmVyeSBlYXJseSByaW5ncyBhbmQgaW4gbWF0dXJlIHNleGVzIG1heSBiZSBkdWUgdG8gYSBzbWFsbGVyIG51bWJlciBvZiB0aGUgcG9wdWxhdGlvbiBiZWluZyB0aGVzZSBjZWxscy4gCgpJdCBtYXkgYWxzbyBiZSBiaW9sb2dpY2FsLCB0aGF0IHRoZXNlIGNlbGxzIGFyZSBsZXNzIGxpa2VseSB0byBhc3NvY2lhdGUgdG8gb25lIGFub3RoZXIgKGFsdGhvdWdoIGxlc3MgbGlrZWx5LCBhcyBkb3VibGV0cyBhcmUgYSByZXN1bHQgb2YgdGhlIHByb2JhYmlsaXR5IG9mIHR3byBjZWxscyBiZWluZyBjYXB0dXJlZCBpbnNpZGUgdGhlIHNhbWUgZHJvcGxldCB0b2dldGhlciBhdCBhIGNlcnRhaW4gbG9hZGluZyBjb25jZW50cmF0aW9uLCByYXRoZXIgdGhhbiB0d28gY2VsbHMgYWxyZWFkeSBiZWluZyB0b2dldGhlciB1cG9uIGRyb3BsZXQgY2FwdHVyZSkuCgpyZW1vdmUgZG91YmxldHM6CmBgYHtyfQojIyBtYWtlIGEgbGlzdCBvZiBzaW5nbGV0IGNlbGxzCmtlZXBfc2luZ2xldHMgPC0gcm93bmFtZXMocGJfc2V4QG1ldGEuZGF0YVtwYl9zZXhAbWV0YS5kYXRhJERGLmNsYXNzaWZpY2F0aW9uc18wLjI1XzAuMDFfNDQwID09ICJTaW5nbGV0IixdKQoKIyMgc3Vic2V0IGludG8gbmV3IHNldXJhdCBvYmplY3QKcGJfc2V4X2ZpbHRlcmVkIDwtIHN1YnNldChwYl9zZXgsIGNlbGxzID0ga2VlcF9zaW5nbGV0cywgc3Vic2V0LnJhdyA9IFRSVUUpCgojIyBjb21wYXJlIG9sZCBhbmQgbmV3IG9iamVjdHMKcGJfc2V4CnBiX3NleF9maWx0ZXJlZApgYGAKCiMgNy4gTGlmZSBDeWNsZSBTdGFnZSAoVXNpbmcgQnVsayBSTkEtU2VxIENvcnJlbGF0aW9uKSB7LnRhYnNldH0KCkFkZCBpbiBidWxrIGRhdGEgcHJlZGljdGlvbnMKCiMjIyBob28gZXQgYWwuCmBgYHtyfQojUGIgUHJlZGljdGlvbiBjb3JyZWxhdGlvbnMgd2l0aCBidWxrIGRhdGEgKGFzZXh1YWwgaG9vKTogCgojTG9hZCBpbiByZXF1aXJlZCBwYWNrYWdlOgpsaWJyYXJ5KEhtaXNjKQojQ29vZXJjZSBleHByZXNzaW9uIGRhdGEgaW50byBhIG1hdHJpeCBhbmQgbG9hZCBpbiB0aGUgcmVmZXJlbmNlIHRpbWVjb3Vyc2UgZGF0YToKeDEwIDwtIGFzLm1hdHJpeChwYl9zZXhfZmlsdGVyZWRAYXNzYXlzJFJOQUBkYXRhKQpyb3duYW1lcyh4MTApIDwtIGdzdWIoIi0iLCAiXyIsIHJvd25hbWVzKHgxMCkpCiNyZWFkIGluIGJ1bGsgZGF0YToKaG9vPC1hcy5tYXRyaXgocmVhZC50YWJsZSgiLi4vZGF0YS9SZWZlcmVuY2UvaG9vX2JlcmcyLnR4dCIsaGVhZGVyPVQsIHJvdy5uYW1lcz0xKSkKI01ha2UgYSBibGFuayBkYXRhZnJhbWUgaW4gd2hpY2ggdG8gYWRkIHByZWRpY3Rpb246CmRmIDwtIGRhdGEuZnJhbWUobWF0cml4KG5jb2wgPSA0LCBucm93ID0gMCkpCmNvbG5hbWVzKGRmKSA8LSBjKCJQcmVkaWN0aW9uKFNwZWFybWFuKSIsInIoU3BlYXJtYW4pIiwiUHJlZGljdGlvbihQZWFyc29ucykiLCJyKFBlYXJzb25zKSIpCiNEbyBjb3JyZWxhdGlvbnMgd2l0aCBidWxrIGRhdGEgdXNpbmcgYm90aCBTcGVhcm1hbiBhbmQgUGVhcnNvbiAoYW5kIHRoZSB0b3AgMTAwMCBnZW5lcyk6CmZvciAoaSBpbiAxOm5jb2woeDEwKSkKewogIHNoYXJlZDwtaW50ZXJzZWN0KHJvdy5uYW1lcyhhcy5tYXRyaXgoaGVhZChzb3J0KHgxMFssaV0sIGRlY3JlYXNpbmc9VFJVRSksMTAwMCkpKSxyb3cubmFtZXMoaG9vKSkKICBzdGVwMDwtcmNvcnIoeDEwW3NoYXJlZCxpXSxob29bc2hhcmVkLDE6MTJdLHR5cGUgPSAic3BlYXJtYW4iKQogIHN0ZXAxPC1hcy5tYXRyaXgodChzdGVwMCRyWzI6MTMsMV0pKQogIHN0ZXAyPC1yY29ycih4MTBbc2hhcmVkLGldLGhvb1tzaGFyZWQsMToxMl0sdHlwZSA9ICJwZWFyc29uIikKICBzdGVwMzwtYXMubWF0cml4KHQoc3RlcDIkclsyOjEzLDFdKSkKICBzdGVwNDwtY2JpbmQoY29sbmFtZXMoc3RlcDEpW3doaWNoLm1heChzdGVwMSldLHN0ZXAxW3doaWNoLm1heChzdGVwMSldLGNvbG5hbWVzKHN0ZXAzKVt3aGljaC5tYXgoc3RlcDMpXSxzdGVwM1t3aGljaC5tYXgoc3RlcDMpXSkKICBjb2xuYW1lcyhzdGVwNCkgPC0gYygiUHJlZGljdGlvbihTcGVhcm1hbikiLCJyKFNwZWFybWFuKSIsIlByZWRpY3Rpb24oUGVhcnNvbnMpIiwicihQZWFyc29ucykiKQogIHJvd25hbWVzKHN0ZXA0KTwtY29sbmFtZXMoeDEwKVtpXQogIGRmPC1yYmluZChkZixzdGVwNCkKfQojV3JpdGUgb3V0IGRhdGEgaW50byBhIGNzdiBmaWxlOgojd3JpdGUuY3N2KGRmcmluZ3IsZmlsZT0iL1VzZXJzL2FyMTkvRGVza3RvcC9QaEQvQVIwNF9HQ1NLT19wcm9qZWN0L0FsbF9tdXRhbnRzX0ZlYl8yMDE4L3ByZWRpY3Rpb25wYmNvbWJpbmVkLmNzdiIpCiNDaGFuZ2UgdGhlIGZvcm1hdCBvZiB0aGUgb3V0cHV0IHRvIG1ha2UgaXQgbW9yZSByZWFkYWJsZToKI2dzdWIoIlBiXyIsIiIsIGRmcmluZ3JbLDFdKSAtIE1ha2UgcHJlZGljdGlvbnMgaW50byAxOGhyLmRhdCBmb3JtYXQ6Cgojc3BlYXJtYW46CmRmWywxXSA8LSBnc3ViKCJQYl8iLCIiLCBkZlssMV0pCiNSZW1vdmUgaHIuZGF0IGZyb20gbGlzdDoKZGZbLDFdIDwtIGdzdWIoImhyLmRhdCIsIiIsIGRmWywxXSkKI0NoZWNrIC0gZGZyaW5nclssMV0KI01ha2UgaW50byBhIG51bWJlcjoKZGZbLDFdIDwtIGFzLm51bWVyaWMoZGZbLDFdKQpkZlssMl0gPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGZbLDJdKSkKCiNwZWFyc29uOgpkZlssM10gPC0gZ3N1YigiUGJfIiwiIiwgZGZbLDNdKQojUmVtb3ZlIGhyLmRhdCBmcm9tIGxpc3Q6CmRmWywzXSA8LSBnc3ViKCJoci5kYXQiLCIiLCBkZlssM10pCiNDaGVjayAtIGRmcmluZ3JbLDFdCiNNYWtlIGludG8gYSBudW1iZXI6CmRmWywzXSA8LSBhcy5udW1lcmljKGRmWywzXSkKZGZbLDRdIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRmWyw0XSkpCiNhZGQgdG8gMTBYIG9iamVjdDoKcGJfc2V4X2ZpbHRlcmVkIDwtIEFkZE1ldGFEYXRhKHBiX3NleF9maWx0ZXJlZCwgbWV0YWRhdGEgPSBkZikKYGBgCgojIyMgS2FzaWEncyBkYXRhCkNhbiBhbHNvIGRvIHdpdGggS2FzaWEncyB0aW1lY291cnNlIGRhdGE6CmBgYHtyfQprYXM8LWFzLm1hdHJpeChyZWFkLnRhYmxlKCIuLi9kYXRhL1JlZmVyZW5jZS9BUDJPRVRDLnR4dCIsaGVhZGVyPVQsIHJvdy5uYW1lcz0xKSkKI01ha2UgYSBibGFuayBkYXRhZnJhbWUgaW4gd2hpY2ggdG8gYWRkIHByZWRpY3Rpb246CmRmcyA8LSBkYXRhLmZyYW1lKG1hdHJpeChuY29sID0gNCwgbnJvdyA9IDApKQpjb2xuYW1lcyhkZnMpIDwtIGMoIklEIiwiUHJlZGljdGlvbiIsInIgKFBlYXJzb24pIikKI0RvIGNvcnJlbGF0aW9ucyB3aXRoIGJ1bGsgZGF0YSB1c2luZyBib3RoIFNwZWFybWFuIGFuZCBQZWFyc29uIChhbmQgdGhlIHRvcCAxMDAwIGdlbmVzKToKZm9yIChpIGluIDE6bmNvbCh4MTApKQp7CiAgc2hhcmVkPC1pbnRlcnNlY3Qocm93Lm5hbWVzKGFzLm1hdHJpeChoZWFkKHNvcnQoeDEwWyxpXSwgZGVjcmVhc2luZz1UUlVFKSwxMDAwKSkpLHJvd25hbWVzKGthcykpCiAgc3RlcDA8LXJjb3JyKHgxMFtzaGFyZWQsaV0sa2FzW3NoYXJlZCwxOjEwXSx0eXBlID0gInNwZWFybWFuIikKICBzdGVwMTwtYXMubWF0cml4KHQoc3RlcDAkclsyOjExLDFdKSkKICBzdGVwMjwtcmNvcnIoeDEwW3NoYXJlZCxpXSxrYXNbc2hhcmVkLDE6MTBdLHR5cGUgPSAicGVhcnNvbiIpCiAgc3RlcDM8LWFzLm1hdHJpeCh0KHN0ZXAyJHJbMjoxMSwxXSkpCiAgc3RlcDQ8LWNiaW5kKGNvbG5hbWVzKHN0ZXAxKVt3aGljaC5tYXgoc3RlcDEpXSxzdGVwMVt3aGljaC5tYXgoc3RlcDEpXSxjb2xuYW1lcyhzdGVwMylbd2hpY2gubWF4KHN0ZXAzKV0sc3RlcDNbd2hpY2gubWF4KHN0ZXAzKV0pCiAgY29sbmFtZXMoc3RlcDQpIDwtIGMoIlByZWRpY3Rpb24oU3BlYXJtYW4pIiwicihTcGVhcm1hbikiLCJQcmVkaWN0aW9uKFBlYXJzb25zKSIsInIoUGVhcnNvbnMpIikKICByb3duYW1lcyhzdGVwNCk8LWNvbG5hbWVzKHgxMClbaV0KICBkZnM8LXJiaW5kKGRmcyxzdGVwNCkKfQojV3JpdGUgb3V0IGRhdGEgaW50byBhIGNzdiBmaWxlOgojd3JpdGUuY3N2KGRmLGZpbGU9Ii9Vc2Vycy9hcjE5L0Rlc2t0b3AvUGhEL0FSMDRfR0NTS09fcHJvamVjdC9BbGxfbXV0YW50c19GZWJfMjAxOC9wcmVkaWN0aW9ua2FzaWFjb21iaW5lZC5jc3YiKQoKI0NoYW5nZSB0aGUgZm9ybWF0IG9mIHRoZSBvdXRwdXQgdG8gbWFrZSBpdCBtb3JlIHJlYWRhYmxlOgojZ3N1YigiUGJfIiwiIiwgZGZzWywxXSkgLSBNYWtlIHByZWRpY3Rpb25zIGludG8gMThoci5kYXQgZm9ybWF0OgpkZnNbLDFdIDwtIGdzdWIoIlgiLCIiLCBkZnNbLDFdKQojTWFrZSBpbnRvIGEgbnVtYmVyOgpkZnNbLDFdIDwtIGFzLm51bWVyaWMoZGZzWywxXSkKI01ha2UgaW50byBhIG51bWJlcjoKZGZzWywyXSA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkZnNbLDJdKSkKCiNnc3ViKCJQYl8iLCIiLCBkZnNbLDFdKSAtIE1ha2UgcHJlZGljdGlvbnMgaW50byAxOGhyLmRhdCBmb3JtYXQ6CmRmc1ssM10gPC0gZ3N1YigiWCIsIiIsIGRmc1ssM10pCiNNYWtlIGludG8gYSBudW1iZXI6CmRmc1ssM10gPC0gYXMubnVtZXJpYyhkZnNbLDNdKQojZGZzWywxXQojTWFrZSBpbnRvIGEgbnVtYmVyOgpkZnNbLDRdIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRmc1ssNF0pKQoKY29sbmFtZXMoZGZzKSA8LSBjKCdQcmVkaWN0aW9uKFNwZWFybWFuKV9LYXNpYScsICdyKFNwZWFybWFuKV9LYXNpYScsICdQcmVkaWN0aW9uKFBlYXJzb24pX0thc2lhJywgJ3IoUGVhcnNvbilfS2FzaWEnKQojYWRkIHRvIFNldXJhdDoKI2FkZCB0byAxMFggb2JqZWN0OgpwYl9zZXhfZmlsdGVyZWQgPC0gQWRkTWV0YURhdGEocGJfc2V4X2ZpbHRlcmVkLCBkZnMpCmBgYAoKIyMjIFZpc3VhbGlzZQoKQ29uZmlybSBsaWZlIGN5Y2xlIGRlc2lnbmF0aW9uczoKYGBge3J9CiMjIHBsb3QKRmVhdHVyZVBsb3QocGJfc2V4X2ZpbHRlcmVkLCBmZWF0dXJlcyA9IGMoIlByZWRpY3Rpb24uU3BlYXJtYW4uX0thc2lhIiwgIlByZWRpY3Rpb24uU3BlYXJtYW4uIikpCmBgYAoKb3B0aW1zZSBVTUFQCmBgYHtyfQojIyBQQ0EgY2FsYwpwYl9zZXhfZmlsdGVyZWQgPC0gUnVuUENBKHBiX3NleF9maWx0ZXJlZCwgZmVhdHVyZXMgPSBWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IHBiX3NleF9maWx0ZXJlZCkpCgojIyBlbGJvdyBwbG90CkVsYm93UGxvdChwYl9zZXhfZmlsdGVyZWQsIG5kaW1zID0gMzAsIHJlZHVjdGlvbiA9ICJwY2EiKQoKIyMgVU1BUCBjYWxjCnBiX3NleF9maWx0ZXJlZCA8LSBSdW5VTUFQKHBiX3NleF9maWx0ZXJlZCwgZGltcyA9IDE6OCwgc2VlZC51c2UgPSAzMDAsIG4ubmVpZ2hib3JzID0gNjAsIG1pbi5kaXN0ID0gMC41LCByZXB1bHNpb24uc3RyZW5ndGggPSAwLjA1LCBsb2NhbC5jb25uZWN0aXZpdHkgPSAyMCkKCiMjIFVNQVAgcGxvdApEaW1QbG90KHBiX3NleF9maWx0ZXJlZCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJpZGVudCIsIGxhYmVsID0gVFJVRSkKYGBgCgoKIyA4LiBTYXZlIGFuZCBFeHBvcnQgey50YWJzZXR9CgpTYXZlIGVudmlyb25tZW50CmBgYHtyfQojIyBUaGlzIHNhdmVzIGV2ZXJ5dGhpbmcgaW4gdGhlIGdsb2JhbCBlbnZpcm9ubWVudCBmb3IgZWFzeSByZWNhbGwgbGF0ZXIKI3NhdmUuaW1hZ2UoZmlsZSA9ICJHQ1NLT18xMFhfUUMuUkRhdGEiKQojbG9hZChmaWxlID0gIkdDU0tPXzEwWF9RQy5SRGF0YSIpCmBgYAoKU2F2ZSBvYmplY3QocykKYGBge3J9CiMjIHNhdmUgUmRhdGEKI3NhdmUocGJfMzBrX3NleF9maWx0ZXJlZCwgcGJfc2V4X2ZpbHRlcmVkLCBmaWxlID0gIlBhcnRfMl9pbnB1dC5SZGF0YSIpCiNsb2FkKGZpbGUgPSAiUGFydF8yX2lucHV0LlJkYXRhIikKCiMjIHNhdmUgUkRTCnNhdmVSRFMocGJfc2V4X2ZpbHRlcmVkLCBmaWxlID0gIi4uL2RhdGFfdG9fZXhwb3J0L3BiX3NleF9maWx0ZXJlZC5SRFMiLCBjb21wcmVzcyA9IEZBTFNFKSAKI3BiX3NleF9maWx0ZXJlZCA8LSByZWFkUkRTKCJwYl9zZXhfZmlsdGVyZWQuUkRTIikKCiMjIFNhdmUgUm9iagojc2F2ZShwYl9zZXhfZmlsdGVyZWQsZmlsZT0icGJfc2V4X2ZpbHRlcmVkLlJvYmoiKQpgYGAKCiMgQXBwZW5kaXggey50YWJzZXR9CgojIyBTZXNzaW9uIEluZm8gCmBgYHtyLCBlY2hvID0gRkFMU0V9CnNlc3Npb25JbmZvKCkKYGBgCg==